Ruby枚举器链接

时间:2014-06-13 07:30:59

标签: ruby list enumerator

在此示例中,

[1, 2, 3].each_with_index.map{|i, j| i * j}
# => [0, 2, 6]

我的理解是,由于each_with_index枚举器被链接到mapmap的行为类似于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中的枚举器链吗?

2 个答案:

答案 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]

由于enum2enum4将相同的元素传递到块中,我们看到这只是做同样事情的两种方式。

这是第三个等效链:

[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_indexwith_index,{ {1}}等等。
  • 当您将块传递给这些方法时,它们将返回不同类型的对象,具体取决于对该特定方法有意义的内容。
    • 对于像map这样的方法,它的目的是找到满足条件的第一个对象,并且将它包装在数组中没有特别意义,因此它会返回该对象。
    • 对于像findselect这样的方法,它们的目的是返回所有相关对象,因此它们不能返回单个对象,并且必须将它们包装在一个数组中(即使是相关对象)碰巧是一个单一的对象。)