11

この質問は、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.nexte.nextを使用して連続する各要素を取得できますが、実行を一時停止してから適切な場所で再開するのはどの程度正確ですか?

の歩留まりをに0.upto置き換えるとFiber.yieldわかりやすいと思いますが、ここではそうではありません。それは昔ながらのことですが、yieldどのように機能しますか?

enumerator.cを見ましたが、私には理解できないほどです。たぶん誰かが、1.8.6スタイルの継続ベースの列挙子ではなく、ファイバーを使用してRubyで実装を提供することができます。これにより、すべてが明確になりますか?

4

4 に答える 4

16

これは、Fibersを使用し、オリジナルとほぼ同じように動作するはずのプレーンなルビー列挙子です。

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]]
于 2009-09-17T09:18:35.463 に答える
4

実際、e = Bunk.new.eachでは、else句は最初は実行されません。代わりに、'if!block_given'句が実行され、列挙子オブジェクトが返されます。列挙子オブジェクトは、ファイバーオブジェクトを内部に保持します。(少なくとも、enumerator.cではそのように見えます)

e.eachを呼び出すと、実行コンテキストを追跡するために内部的にファイバーを使用する列挙子のメソッドが呼び出されます。このメソッドは、ファイバー実行コンテキストを使用してBunk.eachメソッドを呼び出します。ここでのBunk.each呼び出しは、else句を実行し、値を生成します。

イールドがどのように実装されているか、またはファイバーが実行コンテキストをどのように追跡しているかはわかりません。私はそのコードを見ていません。ほとんどすべての列挙子とファイバーマジックはCで実装されています。

ファイバーと歩留まりがどのように実装されているかを本当に尋ねていますか?どのレベルの詳細を探していますか?

私がベースから外れている場合は、私を訂正してください。

于 2009-09-17T00:14:33.970 に答える
1

これはもっと正確だと思います。列挙子でそれぞれを呼び出すことは、元のイテレータメソッドを呼び出すことと同じである必要があります。したがって、元のソリューションをこれに少し変更します。

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
于 2011-09-13T18:05:31.597 に答える
1

他のポスターが指摘したように、私はそれが独自のプライベートな「ファイバー」を作成すると信じています[1.9]。1.8.7(またはバックポートgemを使用する場合は1.8.6)では、何らかの形で同じことを行います(おそらく、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。

于 2009-09-26T14:09:49.427 に答える