【问题标题】:Enumerator behavior with generator block生成器块的枚举器行为
【发布时间】:2017-03-02 16:17:22
【问题描述】:

我在理解 Ruby 枚举器如何处理生成器块,尤其是无限序列的生成器时遇到了一点问题。我正在阅读 The Well Grounded Rubyist,有一个例子是这样的:

a = [1, 2, 3, 4, 5]

e = Enumerator.new do |y|
  total = 0
  until a.empty?
    total += a.pop
    y << total
  end
end

e.take(2)
=> [5, 9]
a
=> [1, 2, 3]

我期望它做的是:它从头到尾迭代可枚举,然后返回结果集的前两个元素,将原始数组留空。但经过一番思考,我意识到这在生成器产生无限序列的情况下是行不通的——迭代永远不会结束。

现在,我知道当从外部可枚举创建时,Enumerator 使用 Fibers 来停止和恢复执行,每次底层 Enumerable 为每个块产生价值(或类似的东西,我只知道一般想法)。但是,如果 Enumerator 是从构造函数和生成器块创建的,它是如何工作的呢?

我曾尝试对底层的本机代码进行一些挖掘,但很快就迷失了方向,因为我的 C 技能至少可以说是低于标准的。根据我内部的理解,each 正在枚举器本身上被调用,take_i 函数作为一个块提供。但我找不到任何有关纤维的参考资料,也无法深入挖掘。

【问题讨论】:

    标签: ruby


    【解决方案1】:

    Enumerator 更像是一个惰性列表计算器,允许您创建理论上无限的列表,但仅在需要时实际计算您需要的列表,运行在构造函数块中传递的循环只是它需要给您的次数你问了什么。因此,在您向 e 请求 5 件事之前,您的示例不会清空 a。

    Ruby 的文档示例具有无限循环的斐波那契数列:https://ruby-doc.org/core-2.2.0/Enumerator.html#method-c-new

    【讨论】:

      【解决方案2】:

      好吧,我想我终于明白了。会发生以下情况:

      在内部,枚举器上的 e.take(n) 被 Ruby 解释为:

      result = []
      e.each do |item|
        result << item
        break if result.size == 2
      end
      result
      

      回到我们初始化枚举器的方式,与构造函数一起给出的整个块是Enumerator::Generator类实例的责任,它是与枚举器本身一起创建的。传递给此块的yEnumerator::Yielder 类的实例。

      当在枚举器上调用each 时,首先执行生成器代码,当涉及到y &lt;&lt; total 部分时,y 通过(产生,好像来自方法定义,但不完全是)这个@987654330 @ 块的值,由 each 调用给出。当这个块完成执行并返回时,控制权返回到生成器代码,生成器代码执行另一个循环,并将新值推送到 yielder,它再次将其生成到 each 块等等。当@987654333 中的条件@block 变为真,一切都停止,从而使原始数组的一部分保持不变。所以这里不需要使用 Fibers,只是在两个代码块之间来回控制一些很酷的让步。

      虽然值得注意的是,我所说的“each 块”并不是真正的 ruby​​ 块,它是 C 函数 take_i,被视为块,传递给 each 方法。

      您可以在此处阅读更多相关信息,包括图表和一些额外信息:http://patshaughnessy.net/2013/4/3/ruby-2-0-works-hard-so-you-can-be-lazy

      【讨论】:

      • 这篇博文是关于 lazy 枚举器的,但您的结论仍然是正确的。请注意,e.take(n) 总是从一开始就评估枚举器的块,这与 e.next 不同,后者通过使用 ... [drum roll] ... 纤维来保留当前状态!
      • 抱歉,这太仓促了,帖子详细解释了EnumeratorEnumerator::Lazy。我只记得后半部分。
      • @Stefan 是的,我想在这种情况下 Fiber 机制如何与 yielder 一起工作非常有趣。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-17
      • 2017-10-25
      • 1970-01-01
      • 1970-01-01
      • 2023-03-04
      • 1970-01-01
      • 2023-04-01
      相关资源
      最近更新 更多