分解

通过将其元素分配给多个变量,可以快速分解任何数组。一个简单的例子:

arr = [1, 2, 3]
# ---
a = arr[0]
b = arr[1]
c = arr[2]
# --- or, the same
a, b, c = arr

使用 splat 运算符(*) 在变量之前放入一个未被其他变量捕获的所有元素的数组。如果没有,则分配空数组。一次分配中只能使用一个 splat:

a, *b = arr       # a = 1; b = [2, 3]
a, *b, c = arr    # a = 1; b = [2]; c = 3
a, b, c, *d = arr # a = 1; b = 2; c = 3; d = []
a, *b, *c = arr   # SyntaxError: unexpected *

分解是安全的,永远不会引发错误。nils 被分配在没有足够元素的地方,在访问超出范围的索引时匹配 [] 运算符的行为:

arr[9000] # => nil
a, b, c, d = arr # a = 1; b = 2; c = 3; d = nil

分解试图隐式地在被分配的对象上调用 to_ary。通过在你的类型中实现此方法,你可以分解它:

class Foo
  def to_ary
    [1, 2]
  end
end
a, b = Foo.new # a = 1; b = 2

如果被分解的对象不是 respond_to? to_ary,则将其视为单元素数组:

1.respond_to?(:to_ary) # => false
a, b = 1 # a = 1; b = nil

分解也可以通过使用 () 分隔的分解表达式代替单独的元素来嵌套

arr = [1, [2, 3, 4], 5, 6]
a, (b, *c), *d = arr # a = 1; b = 2; c = [3, 4]; d = [5, 6]
#   ^^^^^

这实际上与 splat 相反。

实际上,任何分解表达式都可以由 () 分隔。但是对于第一级分解是可选的。

a, b = [1, 2]
(a, b) = [1, 2] # the same thing

边缘情况: 单个标识符不能用作解构模式,无论是外部还是嵌套模式:

(a) = [1] # SyntaxError
a, (b) = [1, [2]] # SyntaxError

数组文字指定给解构表达式时,可以省略外部 []

a, b = [1, 2]
a, b =  1, 2  # exactly the same

这称为并行分配,但它在引擎盖下使用相同的分解。这对于交换变量的值而不使用额外的临时变量特别方便:

t = a; a = b; b = t # an obvious way
a, b = b, a         # an idiomatic way
(a, b) = [b, a]     # ...and how it works

在构建赋值的右侧时捕获值,因此使用与源和目标相同的变量是相对安全的。