Ruby 1.9.1 中的枚举器如何工作?
这个问题不是关于如何在 Ruby 1.9.1 中使用枚举器,而是我很好奇它们是如何工作的。这是一些代码:
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
获取每个连续的元素,但它到底是如何暂停执行然后在正确的位置恢复的呢?
我知道如果将 0.upto
中的产量替换为 Fiber.yield
那么很容易理解,但这里的情况并非如此。这是一个普通的旧yield
,那么它是如何工作的呢?
我查看了 enumerator.c 但它对我来说几乎无法理解。也许有人可以在 Ruby 中提供一个实现,使用纤程,而不是 1.8.6 风格的基于连续的枚举器,这样一切就清楚了?
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
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?
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?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是一个使用 Fibers 的普通 ruby 枚举器,其行为应该与原始枚举器非常相似:
以防万一有人对使用控制流异常感到不安:真正的枚举器也在最后引发 StopIteration,所以我只是模拟原始行为。
用法:
Here's a plain ruby enumerator that uses Fibers and should pretty much behave like the original:
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.
Usage:
实际上,在您的 e = Bunk.new.each 中, else 子句最初并未执行。相反,'if !block_given' 子句执行并返回一个枚举器对象。枚举器对象内部确实保留了一个 Fiber 对象。 (至少在 enumerator.c 中是这样的)
当您调用 e.each 时,它正在调用枚举器上的一个方法,该方法在内部使用纤程来跟踪其执行上下文。此方法使用 Fiber 执行上下文调用 Bunk.each 方法。这里的 Bunk.each 调用确实执行 else 子句并产生值。
我不知道yield是如何实现的,也不知道纤程如何跟踪执行上下文。我还没看过那个代码。几乎所有的枚举器和 Fiber 魔法都是用 C 实现的。
您真的在问 Fiber 和产量是如何实现的吗?您正在寻找什么级别的详细信息?
如果我有偏差,请纠正我。
Actually in your e = Bunk.new.each the else clause is not executed initially. Instead the 'if !block_given' clause executes and returns an enumerator object. The enumerator object does keep a fiber object internally. (At least that is what it looks like in enumerator.c)
When you call e.each it is calling a method on an enumerator which uses a fiber internally to keep track of its execution context. This method calls the Bunk.each method using the fibers execution context. The Bunk.each call here does execut the else clause and yields up the value.
I do not know how yield is implemented or how a fiber tracks the execution context. I haven't looked at that code. Almost all of the enumerator and fiber magic is implemented in C.
Are you really asking how fibers and yield are implemented? What level of detail are you looking for?
If I am off base please correct me.
正如其他发帖者所指出的,我相信它创建了自己的私有“光纤”[在 1.9 中]。在 1.8.7(或者 1.8.6,如果你使用 backports gem)中,它以某种方式做同样的事情(也许因为 1.8 中的所有线程都相当于纤维,它只是使用它们?)
因此在 1.9 和 1.8.x 中,如果你将其中几个链接在一起
a.each_line.map.each_with_index { }
它实际上每行都流经整个链,有点像命令行上的管道
http://pragdave.blogs.pragprog.com/pragdave/2007/12/pipelines-using.html
HTH。
As the other posters noted, I believe it creates its own private "fiber" [in 1.9]. In 1.8.7 (or 1.8.6 if you use the backports gem) somehow or other it does the same thing (perhaps because all threads in 1.8 are the equivalent of fibers, it just uses them?)
Thus in 1.9 and 1.8.x, if you chain several of them together
a.each_line.map.each_with_index { }
It actually flows through that whole chain with each line, kind of like a pipe on the command line
http://pragdave.blogs.pragprog.com/pragdave/2007/12/pipelines-using.html
HTH.
我认为这样会更准确。在枚举器上调用 every 应该与调用原始迭代器方法相同。所以我会稍微改变一下原来的解决方案:
I think this would be more accurate. Calling each on the enumerator should be the same as calling the original iterator method. So I would slightly change the original solution to this: