枚举器如何在 Ruby 1.9.1 中工作? [英] How do Enumerators work in Ruby 1.9.1?
问题描述
这个问题不是关于如何在 Ruby 1.9.1 中使用枚举器,而是我很好奇它们是如何工作的.这是一些代码:
This question is not about how to use Enumerators in Ruby 1.9.1 but rather I am curious how they work. Here is some code:
class Bunk
def initialize
@h = [*1..100]
end
def each
if !block_given?
enum_for(:each)
else
0.upto(@h.length) { |i|
yield @h[i]
}
end
end
end
在上面的代码中我可以使用e = Bunk.new.each
,然后e.next
, e.next
得到每个连续的元素,但它究竟是如何暂停执行然后在正确的位置恢复的?
In the above code I can use e = Bunk.new.each
, and then e.next
, e.next
to get each successive element, but how exactly is it suspending execution and then resuming at the right spot?
我知道如果 0.upto
中的 yield 被替换为 Fiber.yield
那么很容易理解,但这里的情况并非如此.这是一个普通的旧 yield
,那么它是如何工作的呢?
I am aware that if the yield in the 0.upto
is replaced with Fiber.yield
then it's easy to understand, but that is not the case here. It is a plain old yield
, so how does it work?
我查看了 enumerator.c,但对我来说有点难以理解.也许有人可以在 Ruby 中提供一个实现,使用纤程,而不是 1.8.6 风格的基于延续的枚举器,这一切都清楚了吗?
I looked at enumerator.c but it's neigh on incomprehensible for me. Maybe someone could provide an implementation in Ruby, using fibers, not 1.8.6 style continuation-based enumerators, that makes it all clear?
推荐答案
这是一个使用 Fibers 的普通 ruby 枚举器,其行为应该与原来的非常相似:
Here's a plain ruby enumerator that uses Fibers and should pretty much behave like the original:
class MyEnumerator
include Enumerable
def initialize(obj, iterator_method)
@f = Fiber.new do
obj.send(iterator_method) do |*args|
Fiber.yield(*args)
end
raise StopIteration
end
end
def next
@f.resume
end
def each
loop do
yield self.next
end
rescue StopIteration
self
end
end
如果有人对控制流使用异常感到不安:真正的 Enumerator 最后也引发了 StopIteration,所以我只是模拟了原始行为.
And in case anyone is feeling uneasy about using exceptions for control flow: The real Enumerator raises StopIteration at the end, too, so I just emulated the original behaviour.
用法:
>> enum = MyEnumerator.new([1,2,3,4], :each_with_index)
=> #<MyEnumerator:0x9d184f0 @f=#<Fiber:0x9d184dc>
>> enum.next
=> [1, 0]
>> enum.next
=> [2, 1]
>> enum.to_a
=> [[3, 2], [4, 3]]
这篇关于枚举器如何在 Ruby 1.9.1 中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!