如何在普查员1.9.1工作?(How do Enumerators work in Ruby 1.9

2019-08-07 02:12发布

这个问题是不是有关如何在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.nexte.next获得每个连续的元素,但它是如何精确地执行挂起,然后在合适的地方重新开始?

我知道,如果在产量0.upto替换Fiber.yield那么很容易理解,但这里并非如此。 这是一个普通的旧yield ,所以它是如何工作的?

我看着enumerator.c但它的嘶鸣上难以理解我。 也许有人可以提供在Ruby中,实现使用的纤维,而不是1.8.6风格基于连续的统计员,这使得这一切清楚了吗?

Answer 1:

下面是一个使用光纤,应该非常表现得像原来的纯红宝石枚举:

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

而之前有人抱怨异常流量控制:真正的枚举末提高StopIteration异常,也一样,所以我只是仿效原来的行为。

用法:

>> 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]]


Answer 2:

其实在你的E = Bunk.new.each最初不执行else子句。 相反,“如果!block_given”条款执行,并返回一个枚举对象。 枚举器对象确实保持纤维对象内部。 (至少,这是什么样子的enumerator.c)

当你调用e.each它呼吁,它使用光纤内部跟踪其执行上下文的枚举的方法。 此方法调用使用纤维执行上下文Bunk.each方法。 这里的Bunk.each调用不做EXECUT else子句,并产生了价值。

我不知道怎么纤维产量实现或如何跟踪执行上下文。 我没有看过的代码。 几乎所有的枚举和纤维魔术在C.实施

你真的问如何纤维和产量实现? 你在找什么级别的详细信息?

如果我是关闭基地,请大家指正。



Answer 3:

正如其他海报指出,我相信它会创建[1.9]自己的私人“纤维”。 在1.8.7(或1.8.6如果使用backports中的宝石),好歹也做同样的事情(也许是因为在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。



Answer 4:

我认为这将是更准确。 每次调用上枚举应与调用原iterator方法。 因此,我想稍微改变原来的解决方案是:

class MyEnumerator
  include Enumerable

   def initialize(obj, iterator_method)
    @f = Fiber.new do
      @result = obj.send(iterator_method) do |*args|
       Fiber.yield(*args)
      end
      raise StopIteration
    end
   end

   def next(result)
     @f.resume result
   end

   def each
     result = nil
     loop do
      result = yield(self.next(result))
     end
     @result
   end
end


文章来源: How do Enumerators work in Ruby 1.9.1?