从我发现的一个例子中,这段代码计算了数组中与其索引相等的元素数。但是怎么样?
[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
但请帮助我理解这种基于可枚举的风格 - 它很优雅!
答案 0 :(得分:5)
让我们从一个更简单的例子开始:
[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方法。
答案 1 :(得分:2)
答案只需点击一下即可:Enumerator的文档:
大多数[
Enumerator
]方法[但大概也是Kernel#to_enum
和Kernel#enum_for
]有两种形式:一种块形式,其中为枚举中的每个项目评估内容,以及非-block表单,它返回一个新的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:添加“为什么要停在这里?”
答案 2 :(得分:0)
很好的回答@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