From: Ross Bamford Date: 2006-02-16T20:23:25+09:00 Subject: Coroutines (Was: FasterGenerator (#66)) Hi, Well, I've snapped a string on my guitar and my computer won't play MP3s today for some reason, so inevitably boredom started to creep in. I started playing around with the generator stuff, specifically about the coroutine discussion earlier in this thread. Just wanted to see if I could *really* get my head around continuations and all that. It starts with some private methods on Kernel, which are then used to implement the generator, but can be used elsewhere too: module Kernel private def coreset(blk) Thread.current[:"#{blk.inspect.hash}_codone"] = nil end def coyield?(blk) Thread.current[:"#{blk.inspect.hash}_codone"] ? false : true end def coyield(blk, *args) raise "Coroutine exhausted" if Thread.current[:"#{blk.inspect.hash}_codone"] catch :coreturn do next_item = (Thread.current[:coreturn] ||= []).pop if next_item next_item.call else final = blk.call(*args) Thread.current[:"#{blk.inspect.hash}_codone"] = true throw :coreturn, final end end end def coreturn(val) callcc do |return_cc| (Thread.current[:coreturn] ||= []) << return_cc throw :coreturn, val end end end class CoGenerator def initialize(enum = nil, &blk) @blk, @pos = blk, 0 if enum @blk = lambda { enum.each { |e| coreturn e } } end end def rewind @pos = 0 coreset @blk end def next @pos += 1 @current = coyield(@blk) end def current @current end def next? coyield? @blk end def end? !self.next? end def each rewind yield coyield(@blk) while coyield?(@blk) end def pos @pos end end It would be used like: g = CoGenerator.new do coreturn 6 # Some work coreturn 7 # More work coreturn 8 # Yet more work 9 end p g.next while g.next? # -> # 6 # 7 # 8 # 9 Obviously this isn't for the quiz (it'd be dead last, being based on continuations, and it doesn't actually have the Generator API) but just, well, for fun. And something to do. It was fun to write, but it's a toy. I dread to think what it mightn't do properly, or at all :) It's behaviour in multithreaded code could well be a bit counter-intuitive too, I'm afraid I don't know much about how coroutines work in other languages. It times a bit faster than the old callcc generator, because it's using less continuations (just the one) but really I guess it's just a test of the coyield/coreturn stuff. Anyway, enough about that... Music shop will be open now :) -- Ross Bamford - rosco@roscopeco.REMOVE.co.uk