Sal*_*cha 7 ruby iterator enumerator
根据Ruby的文档,each如果没有为to_enum或enum_for方法提供目标方法,则Enumerator对象使用该方法(枚举).现在,我们以下面的猴子补丁及其枚举器为例
o = Object.new
def o.each
yield 1
yield 2
yield 3
end
e = o.to_enum
loop do
puts e.next
end
Run Code Online (Sandbox Code Playgroud)
鉴于Enumerator对象使用该each方法来回答何时next被调用each,每次调用时如何调用该方法next?Enumeartor类是否预加载了所有内容o.each并为枚举创建了本地副本?或者是否有某种Ruby魔法在每个yield语句中挂起操作,直到next在enumeartor上调用?
如果制作了内部副本,它是否是深层副本?那些可以用于外部枚举的I/O对象呢?
我正在使用Ruby 1.9.2.
emb*_*oss 10
它并不完全是魔术,但它仍然是美丽的.a不是制作某种类型的副本,而是Fiber首先each在目标可枚举对象上执行.在接收到下一个对象之后each,Fiber产生该对象,从而将控制返回到Fiber最初恢复的位置.
它很漂亮,因为这种方法不需要可枚举对象的副本或其他形式的"备份",因为人们可以想象通过例如调用#to_a可枚举来获得.使用光纤的协作调度允许在需要时准确地切换上下文,而无需保持某种形式的前瞻.
这一切都发生在C代码中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
Run Code Online (Sandbox Code Playgroud)