您可能会发现分解这些表达式并使用 IRB 或 PRY 来查看 Ruby 正在做什么很有用。让我们从以下开始:
[1,2,3].each_with_index.map { |i,j| i*j }
Let
enum1 = [1,2,3].each_with_index
#=> #<Enumerator: [1, 2, 3]:each_with_index>
我们可以用可枚举#to_a http://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-to_a (or 可枚举#entries http://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-entries) 转换enum1
到一个数组以查看它将传递给下一个枚举器的内容(或者传递给块,如果有的话):
enum1.to_a
#=> [[1, 0], [2, 1], [3, 2]]
这并不奇怪。但enum1
没有块。相反,我们向它发送方法枚举#map http://www.ruby-doc.org/core-2.1.1/Enumerable.html#method-i-map:
enum2 = enum1.map
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:each_with_index>:map>
您可能会认为这是一种“复合”枚举器。该枚举器确实有一个块,因此将其转换为数组将确认它将相同的元素传递到块中enum1
将有:
enum2.to_a
#=> [[1, 0], [2, 1], [3, 2]]
我们看到数组[1,0]
是第一个元素enum2
传递到块中。对此数组应用“消歧”来为块变量分配值:
i => 1
j => 0
也就是说,Ruby正在设置:
i,j = [1,0]
我们现在可以调用enum2
通过发送方法each
与块:
enum2.each { |i,j| i*j }
#=> [0, 2, 6]
接下来考虑:
[1,2,3].map.each_with_index { |i,j| i*j }
We have:
enum3 = [1,2,3].map
#=> #<Enumerator: [1, 2, 3]:map>
enum3.to_a
#=> [1, 2, 3]
enum4 = enum3.each_with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:each_with_index>
enum4.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum4.each { |i,j| i*j }
#=> [0, 2, 6]
Since enum2
and enum4
将相同的元素传递到块中,我们看到这只是做同一件事的两种方法。
这是第三个等效链:
[1,2,3].map.with_index { |i,j| i*j }
We have:
enum3 = [1,2,3].map
#=> #<Enumerator: [1, 2, 3]:map>
enum3.to_a
#=> [1, 2, 3]
enum5 = enum3.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:with_index>
enum5.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum5.each { |i,j| i*j }
#=> [0, 2, 6]
为了更进一步,假设我们:
[1,2,3].select.with_index.with_object({}) { |(i,j),h| ... }
We have:
enum6 = [1,2,3].select
#=> #<Enumerator: [1, 2, 3]:select>
enum6.to_a
#=> [1, 2, 3]
enum7 = enum6.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:select>:with_index>
enum7.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum8 = enum7.with_object({})
#=> #<Enumerator: #<Enumerator: #<Enumerator: [1, 2, 3]:
# select>:with_index>:with_object({})>
enum8.to_a
#=> [[[1, 0], {}], [[2, 1], {}], [[3, 2], {}]]
第一个元素enum8
传入块的是数组:
(i,j),h = [[1, 0], {}]
然后应用消歧来为块变量赋值:
i => 1
j => 0
h => {}
注意enum8
显示在三个元素中的每一个中传递一个空散列enum8.to_a
,但这当然只是因为 Ruby 不知道传入第一个元素后哈希值会是什么样子。