根据 Ruby 的文档,Enumerator 对象使用each
方法(枚举)如果没有提供目标方法to_enum
or enum_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
何时回答的方法next
被调用,如何调用each
方法看起来像,每次next
叫做? Enumeartor 类是否预加载了所有内容o.each
并创建一个本地副本进行枚举?或者是否有某种 Ruby 魔法可以在每个yield 语句处挂起操作,直到next
被调用到枚举器上?
如果制作了内部副本,那么它是深副本吗?可用于外部枚举的 I/O 对象又如何呢?
我正在使用 Ruby 1.9.2。
这并不完全是魔法,但它仍然很美丽。不是制作某种副本,而是Fiber http://www.ruby-doc.org/core-1.9.3/Fiber.html用于首先执行each
在目标可枚举对象上。收到下一个对象后each
, the Fiber
产生这个对象,从而将控制权返回到Fiber
最初已恢复。
它很漂亮,因为这种方法不需要可枚举对象的副本或其他形式的“备份”,正如人们可以想象的那样,例如通过调用#to_a
于可数。与纤程的协作调度允许在需要时准确地切换上下文,而不需要保持某种形式的前瞻。
这一切都发生在C code https://github.com/ruby/ruby/blob/trunk/enumerator.c#L546-L605 for 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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)