環境

先日、Schemeでジェネレータの実装を試みたものを紹介した。
http://d.hatena.ne.jp/SaitoAtsushi/20060806/1154790343
しかし、これには無限ループに陥いる場合があることがわかっている。自分では原因をつきとめられなかったので本家のWikiで質問したところ、詳細な解説と共に改良版も提示してくれた。
http://practical-scheme.net/wiliki/wiliki.cgi?Scheme%3agenerator%e3%81%a8do%e3%81%a8while
一日もたたない内にここまでしてくれるとはありがたい話だ。初学者なりに、興味を持ってもらえる質問が出来たらしい。
で、根本的な問題はループを最初に回ったときに補足した継続を使い回している点にある。それがループにdo構文を使った場合とwhile構文を使った場合の差として表われたわけだ。
問題が起こる場合のループはこんな場合である。

(let loop ((i 0))
  (if (< i 10)
      (loop (+ i 1))))

ここでループカウンタに使っている変数iは毎回新しいiである。ループ毎の変数iは独立した存在なのだ。にも関わらず、私が書いたジェネレータは最初にループを回った時の継続を捕捉しているわけであるから、その継続に戻るといつまでたっても最初のループを繰り返すことになってしまうわけである。
では問題が起こらずに動作する場合はどんなループだろうか。

(let ((i 0))
  (define (loop)
    (if (< i 10)
        (begin (inc! i)
               (loop))))
  (loop))

ループ毎の変数iはあくまでも同じ存在であり、それの内容が更新されている。なので、最初のループで補足した継続に戻っても変数iは更新されているのでカウントできているというワケ。継続の捕捉は束縛を保存するだけで、破壊的な変更をすると、その後にその継続に戻っても破壊された箇所は巻き戻らない。だからたまたまその状況だけでジェネレータが動いたのを見てちゃんと動作していると誤解してしまったという次第。
どの環境において継続を捕捉したのかをちゃんと意識しようという教訓。
Document ID: 998b2a3163230fa3b4d0c92c93cc152b