to_enum(:method)如何在此处接收其块?

Dea*_*ffe 6 ruby enumerable enumerator

从我发现的一个例子中,这段代码计算了数组中与其索引相等的元素数.但是怎么样?

[4, 1, 2, 0].to_enum(:count).each_with_index{|elem, index| elem == index}
Run Code Online (Sandbox Code Playgroud)

我不可能只通过链接来完成它,链中的评估顺序令人困惑.

我理解的是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
Run Code Online (Sandbox Code Playgroud)

但请帮助我理解这种基于可枚举的风格 - 它很优雅!

小智 5

让我们从一个更简单的例子开始:

[4, 1, 2, 0].count{|elem| elem == 4}
=> 1
Run Code Online (Sandbox Code Playgroud)

所以这里count方法返回1,因为该块对于数组的一个元素(第一个元素)返回true.

现在让我们来看看你的代码.首先,Ruby在调用to_enum时创建一个枚举器对象:

[4, 1, 2, 0].to_enum(:count)
=> #<Enumerator: [4, 1, 2, 0]:count>
Run Code Online (Sandbox Code Playgroud)

这里枚举器正在等待使用[4,1,2,0]数组和count方法执行迭代.枚举器就像一个挂起的迭代,等待稍后发生.

接下来,在枚举器上调用each_with_index方法,并提供一个块:

...each_with_index{|elem, index| elem == index}
Run Code Online (Sandbox Code Playgroud)

这将调用您在上面创建的枚举器对象上的Enumerator#each_with_index方法.Enumerator#each_with_index所做的是使用给定的块启动挂起的迭代.但它也将索引值与迭代中的值一起传递给块.由于挂起的迭代设置为使用count方法,因此枚举器将调用Array#count.这将每个元素从数组传递回枚举器,枚举器将它们与索引一起传递到块中.最后,Array#count计算真实值,就像上面的简单示例一样.

对我来说,理解这一点的关键是你正在使用Enumerator#each_with_index方法.


Car*_*and 2

只需点击一下即可找到答案:Enumerator的文档:

大多数 [ Enumerator] 方法 [但可能还有Kernel#to_enumKernel#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
Run Code Online (Sandbox Code Playgroud)

特别注意第三行的 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
Run Code Online (Sandbox Code Playgroud)

(编辑 1:将文档中的示例替换为与问题相关的示例。编辑 2:添加“为什么停在这里?)