Generator の再発明

思考実験: returnを関数と思ってみる話 - d.y.d.をきっかけにちょっぴり継続がわかりました。
そこで、Generatorを再発明してみました。

class MyGenerator
  class StopIteration < IndexError; end
  def initialize(obj, method_name=:each)
    @obj = obj
    @method_name = method_name
    @cont = nil
  end
  
  def next
    callcc do |my_return|
      if @cont
        @cont.call(my_return)
        raise 'not reached'
      end
      return_ = nil
      @obj.__send__(@method_name) do |*args|
        ret = return_ || my_return
        return_ = callcc do |c|
          @cont = c
          ret.call(*args)
        end
      end
      raise StopIteration, 'iteration reached at end'
    end
  end
  
  def rewind
    @cont = nil
  end
end

追記 (2009-03-15T07:24:30+09:00)

next の最後で raise したら初回の next() の環境で raise が起こってしまうことに気づきました。たとえば、次のプログラムで無限ループになります。むーチェックが足りなかったですね。
Ruby code - 36 lines - codepad

g = MyGenerator.new([1,2,3])
p((g.next rescue $!))
p((g.next rescue $!))
p((g.next rescue $!))
p((g.next rescue $!))