Here's my solution. Mine is pretty much similar in spirit to Caleb's
solution, except that we use different lock mechanisms. My first take
was to replace the Continuation with a thread and a SizedQueue. That is,
in the #initialize, I create a new thread with the given block (or the
one I generate with the given enum), which will eventually be blocked
when writing to the queue with the size of 1 in #yield. Once #next
dequeues the head, the #yield thread continues, etc.

This passed James's TC_Generator test suite, but miserably failed on
Luke's "shared state" test, although the code was supposed to handle the
case.

It turned out, if SizedQueue of size one is the only blocking mechanism,
you have two values waiting at the queue's door; one in the queue, the
other right before the queue, waiting to be unlocked. This made the
reponse to Luke's test 1, 1, 2, 3, 4 (and then 10, 10, 10, ... if I
increase the repetition). I needed to make the thread stop right after
it enqueued the value until #next consumes it.

My solution was to get rid of SizedQueue and to use a Mutex and a
ConditionVariable to accomplish just that. At that point I saw Caleb's
solution and thought that starting and stopping a thead should be much
slower than using Mutexes and Cond_Vars. To my surprise, that wasn't the
case. Mutex mechanism was much slower than Caleb's thread switching
solution.

Anyways, here's the code. Benchmark follows the code (I ran on a slower
notebook).

Thanks James for the nice quiz.

Jesse

#!/usr/bin/env ruby
require 'thread'

class Generator
  include Enumerable

  def initialize(enum = nil, &block)
    if enum
      @block = proc { |g|
        enum.each { |x| g.yield x }
      }
    else
      @block = block
    end

    @index = 0
    @queue = []
    @q_access = Mutex.new
    @q_consumed = ConditionVariable.new

    @thread = Thread.new(self, &@block)

    self
  end

  def yield(value)
    @q_access.synchronize {
      @queue << value
      @q_consumed.wait(@q_access)
    }

    self
  end

  def end?()
    Thread.pass while @queue.empty? && @thread.alive?
    @queue.empty? && !@thread.alive?
  end

  def next?()
    !end?
  end

  def index()
    @index
  end

  def pos()
    @index
  end

  def next()
    if end?
      raise EOFError, "no more elements available"
    end
    ret = nil
    @q_access.synchronize {
      @index += 1
      ret = @queue.shift
      @q_consumed.signal
    }

    ret
  end

  def current()
    if end?
      raise EOFError, "no more elements available"
    end

    @queue.first
  end

  def rewind()
    initialize(nil, &@block) if @index.nonzero?

    self
  end

  def each
    rewind

    until end?
      yield self.next
    end

    self
  end
end

###
### tests = 10
### enum = (1..1000).to_a
###

### Construction ###

Rehearsal
----------------------------------------------------------------
Old callcc Generator           0.000000   0.000000   0.000000 (
0.003000)
Caleb's SynchronousGenerator   0.000000   0.000000   0.000000 (
0.003000)
Jesse's FasterGenerator        0.010000   0.010000   0.020000 (
0.016000)
------------------------------------------------------- total:
0.020000sec

                                   user     system      total
real
Old callcc Generator           0.000000   0.010000   0.010000 (
0.003000)
Caleb's SynchronousGenerator   0.000000   0.000000   0.000000 (
0.002000)
Jesse's FasterGenerator        0.000000   0.000000   0.000000 (
0.003000)

### next() ###

Rehearsal
----------------------------------------------------------------
Old callcc Generator           4.116000   0.270000   4.386000 (
4.438000)
Caleb's SynchronousGenerator   1.181000   0.010000   1.191000 (
1.194000)
Jesse's FasterGenerator        2.674000   0.000000   2.674000 (
2.831000)
------------------------------------------------------- total:
8.251000sec

                                   user     system      total
real
Old callcc Generator           4.066000   0.010000   4.076000 (
4.099000)
Caleb's SynchronousGenerator   1.212000   0.000000   1.212000 (
1.222000)
Jesse's FasterGenerator        2.704000   0.000000   2.704000 (
2.706000)

-- 
Posted via http://www.ruby-forum.com/.