ささだんが 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
}
最近のコメント