Ruby的Enumerator对象如何在内部迭代器上进行外部迭代? [英] How does Ruby's Enumerator object iterate externally over an internal iterator?

查看:121
本文介绍了Ruby的Enumerator对象如何在内部迭代器上进行外部迭代?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据Ruby的文档,如果没有为 to_enum 每个方法(枚举) c $ c>或 enum_for 方法。现在,我们以下面的猴子补丁及其枚举器为例

As per Ruby's documentation, the Enumerator object uses the each method (to enumerate) if no target method is provided to the to_enum or enum_for methods. Now, let's take the following monkey patch and its enumerator, as an example

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 时的方法,如何调用每个方法,每次调用 next ? Enumeartor类是否预先加载 o.each 的所有内容,并为枚举创建本地副本?或者是否有某种Ruby魔术在每个yield语句中挂起操作,直到在enumeartor上调用 next

Given that the Enumerator object uses the each method to answer when next is called, how do calls to the each method look like, every time next is called? Does the Enumeartor class pre-load all the contents of o.each and creates a local copy for enumeration? Or is there some sort of Ruby magic that hangs the operations at each yield statement until next is called on the enumeartor?

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

If an internal copy is made, is it a deep copy? What about I/O objects that could be used for external enumeration?

我正在使用Ruby 1.9.2。

I'm using Ruby 1.9.2.

推荐答案

这不是魔术,但它仍然是美丽的。 光纤,而不是制作某种副本 用于首先在目标可枚举对象上执行每个。收到每个的下一个对象后, Fiber 会产生此对象,从而将控制权返回到 Fiber 最初恢复。

It's not exactly magic, but it is beautiful nonetheless. Instead of making a copy of some sort, a Fiber is used to first execute each on the target enumerable object. After receiving the next object of each, the Fiber yields this object and thereby returns control back to where the Fiber was resumed initially.

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

It's beautiful because this approach doesn't require a copy or other form of "backup" of the enumerable object, as one could imagine obtaining by for example calling #to_a on the enumerable. The cooperative scheduling with fibers allows to switch contexts exactly when needed without the need to keep some form of lookahead.

这一切都发生在 C代码 枚举器。一个显示大致相同行为的纯Ruby版本可能如下所示:

It all happens in the C code for Enumerator. A pure Ruby version that would show roughly the same behavior could look like this:

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

这篇关于Ruby的Enumerator对象如何在内部迭代器上进行外部迭代?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆