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 $!))