6

从我发现的一个示例中,此代码计算数组中等于其索引的元素的数量。但是如何?

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

我不能只用链接来做到这一点,并且链中的评估顺序令人困惑。

我的理解是我们正在使用重载Enumerable#count,如果给出一个块,则计算产生真值的元素数量。我看到这each_with_index具有该项目是否等于它的索引的逻辑。

我不明白的是如何each_with_index成为 的 block 参数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

3 回答 3

4

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

[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 方法。

于 2013-10-21T22:25:43.160 回答
2

只需单击一下即可得到答案:Enumerator的文档:

大多数 [ Enumerator] 方法 [但可能还有Kernel#to_enumand ] 有两种形式:一种为枚举中的每个项目评估内容的块形式,以及一种返回新包装迭代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:添加了“为什么停在这里?)

于 2013-10-21T19:05:56.790 回答
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
于 2013-10-21T22:02:41.407 回答