Ruby的Enumerator对象如何在内部迭代器上进行外部迭代?

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

 o = Object.new def o.each yield 1 yield 2 yield 3 end e = o.to_enum loop do puts e.next end 

鉴于Enumerator对象在调用next时使用each方法来回答,每次调用next时,如何调用each方法? Enumeartor类是否预先加载o.each的所有内容并为枚举创建本地副本? 或者是否有某种Ruby魔术在每个yield语句中挂起操作,直到在enumeartor上调用next?

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

我正在使用Ruby 1.9.2。

它并不完全是魔术,但它仍然是美丽的。 不使用某种类型的副本,而是使用Fiber首先在目标可枚举对象上执行each副本。 在接收到each物体的下一个物体后, Fiber产生该物体,从而将控制返回到最初恢复Fiber

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

这一切都发生在Enumerator的C代码中。 一个显示大致相同行为的纯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