ささだんが 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
ブロックの引数を必要としなくなる。この方式のメリットは、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 }
最近のコメント