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_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

特别注意 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) 如何在这里接收它的块?的主要内容,如果未能解决你的问题,请参考以下文章

ruby JRuby的枚举器#to_enum将实例变量重新绑定到另一个上下文?

如何从烧瓶中接收.js文件上的jsonify变量

如何使用警报管理器将数据从片段传递到广播接收器

vue为啥method值不改

PHP 后台怎么接收post请求的参数

如何发送和接收 JWT 令牌?