脳ざらし紀行


2007-06-16

_ Fiber で generator

ささだんが ruby-dev に投げたやつを、ruby-dev のえんどうさんの投稿などを参考に改良。

show Generator.new{|g|
  g.yield 1
  show Generator.new{|g2|
    g2.yield 2
    g.yield 3
    g2.yield 4
  }
  g.yield 5
}

みたいな Generator の入れ子に対応するには、Generator#yield で Fiber.current を記憶する必要があると思う

class Generator
  def initialize enum = nil, &block
    @finished = false
    @index = 0
    @enum = enum
    @block = block
    @parent = Fiber.current
    
    @fib = if block_given?
      Fiber.new{
        yield self
        @finished = true
        @parent.yield
      }
    else
      Fiber.new{
        enum.each{|e|
          @e = e
          @parent.yield
        }
        @finished = true
        @parent.yield
      }
    end
    @fib.yield
  end

  def next?
    !@finished
  end

  def next
    raise "No element remained" if @finished
    ret = @e
    @index += 1
    @parent = Fiber.current
    @fib.yield
    ret
  end

  def end?
    @finished
  end

  def yield value
    @e = value
    @fib = Fiber.current
    @parent.yield
  end

  def current
    @e
  end

  def index
    @index
  end

  alias pos index

  def rewind    
    initialize(@enum, &@block) if @index.nonzero?
    self
  end
end

def show g
  while g.next?
    puts "#{Fiber.current}, #{g.current}"
    g.next
  end
end 

show Generator.new{|g|
  g.yield 1
  show Generator.new{|g2|
    g2.yield 2
    g.yield 3
    g2.yield 4
  }
  g.yield 5
}

 gg = Generator.new{|g|
  g.yield :a
  g.yield :b
  g.yield :c
 }

 Fiber.new do
   show gg
 end.yield

_ semi-coroutine で generator

ブロックの引数を必要としなくなる。この方式のメリットは、Generator の入れ子などというものが、以下のように、書こうとしても書けなくなるところ。というか、Generator のブロックの引数って、必要ないならそれにこしたことない代物だよね。

show Generator.new{
  Generator.yield 1
  show Generator.new{
    Generator.yield 2
    Generator.yield 3
    Generator.yield 4
  }
  Generator.yield 5
}
class CoLua < Fiber  
  @@h = {}
  attr_accessor :parent   
  alias :__yield__ :yield
  def resume(*v)
    h = @@h[Thread.current] ||= {}
    raise "can not resume parent fibers" if h[self]
    begin
      @parent = Fiber.current
      h[Fiber.current] = true
      self.__yield__(*v)
    ensure
      @parent = nil
      h.delete(Fiber.current)
    end
  end

  def self.yield(*v)
    CoLua.current.parent.yield(*v)
  end
end

class Generator

  def Generator.yield(*v)
    CoLua.yield(*v)
  end
  
  def initialize enum = nil, &block
    @finished = false
    @index = 0
    @enum = enum
    @block = block
    
    @fib = if block_given?
      CoLua.new{
        yield
        @finished = true
        CoLua.yield
      }
    else
      CoLua.new{
        enum.each{|e|
          CoLua.yield e
        }
        @finished = true
        CoLua.yield
      }
    end
    @e = @fib.resume
  end

  def next?
    !@finished
  end

  def next
    raise "No element remained" if @finished
    ret = @e
    @index += 1
    @e = @fib.resume
    ret
  end

  def end?
    @finished
  end

  def current
    @e
  end

  def index
    @index
  end

  alias pos index

  def rewind    
    initialize(@enum, &@block) if @index.nonzero?
    self
  end
end
def show g
  while g.next?
    puts "#{Fiber.current}, #{g.current}"
    g.next
  end
end

show Generator.new{
  Generator.yield 1
  show Generator.new{
    Generator.yield 2
    Generator.yield 3
    Generator.yield 4
  }
  Generator.yield 5
}
お名前:
E-mail:
コメント:
本日のリンク元

最近のコメント

2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|04|05|
2011|04|
2012|03|07|
2013|01|02|07|
トップ «前の日記(2007-06-14) 最新 次の日記(2007-06-24)» 編集