在此示例中,
[1, 2, 3].each_with_index.map{|i, j| i * j}
# => [0, 2, 6]
我的理解是,由于each_with_index
枚举器被链接到map
,map
的行为类似于each_with_index
,它通过在块内传递索引,并返回一个新数组。
为此,
[1, 2, 3].map.each_with_index{|i, j| i * j}
# => [0, 2, 6]
我不确定如何解释它。
在此示例中,
[1, 2, 3, 4].map.find {|i| i == 2}
# => 2
我希望输出为[2]
,假设map
被链接到find
,而map
将返回一个新数组。
另外,我看到了:
[1, 2, 3, 4].find.each_with_object([]){|i, j| j.push(i)}
# => [1]
[1, 2, 3, 4].each_with_object([]).find{|i, j| i == 3}
# => [3, []]
你能让我知道如何解释和理解Ruby中的枚举器链吗?
答案 0 :(得分:11)
您可能会发现打破这些表达式并使用IRB或PRY来查看Ruby正在做什么很有用。让我们从:
开始[1,2,3].each_with_index.map { |i,j| i*j }
让
enum1 = [1,2,3].each_with_index
#=> #<Enumerator: [1, 2, 3]:each_with_index>
我们可以使用Enumerable#to_a(或Enumerable#entries)将enum1
转换为数组,以查看它将传递给下一个枚举器的内容(如果有一个,则查看它) :
enum1.to_a
#=> [[1, 0], [2, 1], [3, 2]]
那里不足为奇。但enum1
没有阻止。相反,我们发送方法Enumerable#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 }
我们有:
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]
由于enum2
和enum4
将相同的元素传递到块中,我们看到这只是做同样事情的两种方式。
这是第三个等效链:
[1,2,3].map.with_index { |i,j| i*j }
我们有:
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| ... }
我们有:
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在第一个元素之后不知道哈希的样子传入。
答案 1 :(得分:1)
您提及的方法是在Enumerable
个对象上定义的。这些方法的行为会有所不同,具体取决于您是否传递了一个块。
Enumerator
对象,您可以将其他方法链接到each_with_index
,with_index
,{ {1}}等等。map
这样的方法,它的目的是找到满足条件的第一个对象,并且将它包装在数组中没有特别意义,因此它会返回该对象。find
或select
这样的方法,它们的目的是返回所有相关对象,因此它们不能返回单个对象,并且必须将它们包装在一个数组中(即使是相关对象)碰巧是一个单一的对象。)