7

根据 Ruby 的文档,如果没有为or方法each提供目标方法,则 Enumerator 对象使用该方法(枚举)。现在,让我们以下面的猴子补丁及其枚举器为例to_enumenum_for

o = Object.new
def o.each
    yield 1
    yield 2
    yield 3
end
e = o.to_enum

loop do
  puts e.next
end

鉴于 Enumerator 对象在被调用each时使用方法来回答,那么对方法的调用看起来如何,每次被调用?Enumeartor 类是否预加载了枚举的所有内容并创建了本地副本以进行枚举?或者是否有某种 Ruby 魔法可以在每个 yield 语句处挂起操作,直到在 enumeartor 上被调用?nexteachnexto.eachnext

如果制作了内部副本,是否是深层副本?可用于外部枚举的 I/O 对象呢?

我正在使用 Ruby 1.9.2。

4

1 回答 1

11

这并不完全是魔法,但它仍然很漂亮。a 不是制作某种副本,而是Fiber首先each在目标可枚举对象上执行。在接收到 的下一个对象后eachFiber产生这个对象,从而将控制权返回到Fiber最初恢复的位置。

这很漂亮,因为这种方法不需要可枚举对象的副本或其他形式的“备份”,正如人们可以想象的那样,例如通过调用可枚举对象来获得#to_a。与光纤的协作调度允许在需要时准确地切换上下文,而无需保持某种形式的前瞻。

这一切都发生Enumerator. 显示大致相同行为的纯 Ruby 版本可能如下所示:

class MyEnumerator
  def initialize(enumerable)
    @fiber = Fiber.new do
      enumerable.each { |item| Fiber.yield item }
    end
  end

  def next
    @fiber.resume || raise(StopIteration.new("iteration reached an end"))
  end
end

class MyEnumerable
  def each
    yield 1
    yield 2
    yield 3
  end
end

e = MyEnumerator.new(MyEnumerable.new)
puts e.next # => 1
puts e.next # => 2
puts e.next # => 3
puts e.next # => StopIteration is raised
于 2012-06-15T21:14:01.427 回答