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