From: Akinori MUSHA Date: 2001-09-25T18:53:22+09:00 Subject: [ruby-dev:14816] Generator  Python 2.2 では iterator と generator というのが導入されていて、 Ruby からの刺激も受けていると思われ、感慨深い感じがします。  これらはいずれも外部イテレータなのですが、前者は Java や C++ と同じオーソドックスな外部イテレータであり、特筆すべきことはあり ません。  一方、後者は内部イテレータとの融合といった感じでなかなか面白い です。そのアイデアは Icon から取られたようで、実装は例によって Python 的ハッカリーなのですが、 def generate_ints(N): for i in range(N): yield i のように新設の yield というキーワードを呼ぶ関数を定義すると、 インタープリタが自動検知し、 generator 関数と見なします。  generator 関数を評価すると、 generator オブジェクトが得られ ます。そのオブジェクトには next メソッドがあり、これを呼ぶと 関数の中身が実行されるのですが、 yield に当たるとそこで実行を 停止して yield に渡された値を返します。  そして再び next を呼ぶと関数の実行が再開され、次の yield が 呼ばれれば実行が停止されて一つ値を返します。関数の実行が終了 すると、 StopIteration 例外が発生し、繰り返しは終了します。  ということで、これを見て、 Ruby でも callcc を使えばできるの ではないかと思い、以前に一度 Thread を使った実装で挫折した、 内部イテレータ(Enumerable)の外部イテレータ化を実装してみました。  ついでに、これを使うと複数の Enumerable を同期して回すことが できるということで、 Synchronizer というのも作ってみました。 http://www.ruby-lang.org/~knu/cgi-bin/cvsweb.cgi/rough/lib/generator.rb?rev=1.1  Generator はブロックまたは Enumerable なオブジェクトを渡して 生成し、 end? と next メソッドで制御します。  Enumerable の場合はこんな感じ。 f = open("foo.txt") g = Generator.new(f) until g.end? print g.next end ブロックの場合は、引数として Generator オブジェクトが渡される ので、値を返すときはその yield メソッドを呼びます。 g = Generator.new { |g| for i in 1..9 for j in 1..9 g.yield i * j end end } until g.end? puts g.next end あとで index, rewind 等も実装してみたいと思います。  Synchronizer の方は、複数の Enumerable を同期してイテレートする Enumerable なオブジェクトを作るクラスです。Generator を使って実装 しています。 f1 = open("/etc/passwd") f2 = open("/etc/group") Synchronizer.new(f1,f2).each { |l1, l2| print "1: #{l1}" if l1 print "2: #{l2}" if l2 }  もう少し磨いた上で標準ライブラリに入れてもよいかなと思い rough に 放り込んでみたんですが、いかがでしょうか? -- / /__ __ Akinori.org / MUSHA.org / ) ) ) ) / FreeBSD.org / Ruby-lang.org Akinori MUSHA aka / (_ / ( (__( @ iDaemons.org / and.or.jp "Freeze this moment a little bit longer, make each impression a little bit stronger.. Experience slips away -- Time stand still"