to_enum(:method) 如何在这里接收它的块?
Posted
技术标签:
【中文标题】to_enum(:method) 如何在这里接收它的块?【英文标题】:How does to_enum(:method) receive its block here? 【发布时间】:2013-10-30 05:07:24 【问题描述】:从我找到的一个示例中,此代码计算数组中等于其索引的元素的数量。但是如何?
[4, 1, 2, 0].to_enum(:count).each_with_index|elem, index| elem == index
我不能只使用链接来完成它,并且链中的评估顺序令人困惑。
我的理解是我们正在使用 Enumerable#count
的重载,如果给出一个块,它会计算产生真值的元素的数量。我看到each_with_index
具有该项目是否等于它的索引的逻辑。
我不明白each_with_index
如何成为count
的块参数,或者为什么each_with_index
的工作方式就像直接在[4,1,2,0]
上调用一样。如果map_with_index
存在,我可以这样做:
[4,1,2,0].map_with_index |e,i| e==i ? e : nil.compact
但请帮助我理解这种基于可枚举的风格 - 它很优雅!
【问题讨论】:
[4,1,2,0].map.with_index |e,i| e==i ? e : nil.compact
甚至:[4,1,2,0].select.with_index |e,i| e==i
@SergioTulentsev,不错的选择。最右边的块的返回值是否直接传递给从 select 返回的“select”迭代器?有多少被链接起来有关系吗?
【参考方案1】:
让我们从一个更简单的例子开始:
[4, 1, 2, 0].count|elem| elem == 4
=> 1
所以这里 count 方法返回 1,因为块对数组的一个元素(第一个元素)返回 true。
现在让我们看看您的代码。首先,当我们调用 to_enum 时,Ruby 会创建一个枚举器对象:
[4, 1, 2, 0].to_enum(:count)
=> #<Enumerator: [4, 1, 2, 0]:count>
这里的枚举器正在等待执行迭代,使用 [4, 1, 2, 0] 数组和 count 方法。枚举器就像一个待处理的迭代,等待稍后发生。
接下来,调用枚举器的 each_with_index 方法,并提供一个块:
...each_with_index|elem, index| elem == index
这将调用您在上面创建的枚举器对象的 Enumerator#each_with_index 方法。 Enumerator#each_with_index 所做的是使用给定的块开始挂起的迭代。但它也会将索引值与迭代中的值一起传递给块。由于挂起的迭代被设置为使用 count 方法,枚举器将调用 Array#count。这会将数组中的每个元素传递回枚举器,枚举器将它们与索引一起传递到块中。最后,Array#count 对真实值进行计数,就像上面更简单的示例一样。
对我来说,理解这一点的关键是您使用的是 Enumerator#each_with_index 方法。
【讨论】:
这就是我一直在寻找的解释——设置挂起的迭代器和将块传递给 each_with_index 所做的迭代器之间的区别。绝对赞成@Sergio! 是的,Dean,这是一个很棒的编辑,但你不能为它奖励 @Sergio 积分。 @CarySwoveland:对,我喜欢获得荣誉,但我也喜欢获得荣誉 :)【参考方案2】:只需点击一下即可获得答案:Enumerator 的文档:
大多数 [
Enumerator
] 方法 [但可能还有Kernel#to_enum
和Kernel#enum_for
] 有两种形式:一种为枚举中的每个项目评估内容的块形式,以及一种返回新的Enumerator
包装迭代。
这里是第二个:
enum = [4, 1, 2, 0].to_enum(:count) # => #<Enumerator: [4, 1, 2, 0]:count>
enum.class # => Enumerator
enum_ewi = enum.each_with_index
# => #<Enumerator: #<Enumerator: [4, 1, 2, 0]:count>:each_with_index>
enum_ewi.class # => Enumerator
enum_ewi.each |elem, index| elem == index # => 2
特别注意 irb 从第三行返回。它继续说,“这允许您将枚举器链接在一起。”并以map.with_index
为例。
为什么要停在这里?
enum_ewi == enum_ewi.each.each.each # => true
yet_another = enum_ewi.each_with_index
# => #<Enumerator: #<Enumerator: #<Enumerator: [4, 1, 2, 0]:count>:each_with_index>:each_with_index>
yet_another.each_with_index |e,i| puts "e = #e, i = #i"
e = [4, 0], i = 0
e = [1, 1], i = 1
e = [2, 2], i = 2
e = [0, 3], i = 3
yet_another.each_with_index |e,i| e.first.first == i # => 2
(编辑 1:将文档中的示例替换为与该问题相关的一个示例。编辑 2:添加了“为什么停在这里?)
【讨论】:
已接受,为您节省一些时间 Cary :) 很好的例子,感谢您最初找到最有帮助的文档部分。【参考方案3】:很好的答案@Cary.. 我不确定该块是如何通过对象链的,但尽管看起来,该块是由 count
方法执行的,就像在这个堆栈跟踪中一样,甚至尽管它的变量绑定到each_with_index
产生的变量
enum = [4, 1, 2, 0].to_enum(:count)
enum.each_with_index|e,i| raise "--" if i==3; puts e; e == i
4
1
2
RuntimeError: --
from (irb):243:in `block in irb_binding'
from (irb):243:in `count'
from (irb):243:in `each_with_index'
from (irb):243
【讨论】:
以上是关于to_enum(:method) 如何在这里接收它的块?的主要内容,如果未能解决你的问题,请参考以下文章