バリケンのRuby日記 RSSフィード

2007-06-03

[] Rubyの「継続(Continuation)」(3)  Rubyの「継続(Continuation)」(3) - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyの「継続(Continuation)」(3) - バリケンのRuby日記  Rubyの「継続(Continuation)」(3) - バリケンのRuby日記 のブックマークコメント

昨日のエントリの続きだよ。

「継続(Continuation)オブジェクト」をcallccブロックの外に持ち出す

昨日の「Rubyの最短FizzBuzzの解説、の補足」でもちょっと書いたけど、「ブロックの外ですでに代入された(宣言された)変数は、ブロックの中からも参照できる」よ。この性質を使うことで、継続(Continuation)オブジェクトcallccブロックの外に持ち出すことができるよ。

cont = nil
p cont.class #=> NilClass
callcc do |c|
  cont = c
end
p cont.class #=> Continuation

callccブロックの中」で「継続(Continuation)オブジェクトcallする」と、「callccの評価が終わった世界」にジャンプするんだったよね。

じゃあ、ブロックの外に持ち出してしまった「継続(Continuation)オブジェクト」をcallすると、どうなると思う?なんと、やっぱり「callccの評価が終わった時点の世界」にジャンプするよ。つまり、過去に向かってジャンプしちゃうんだ。

だから、次のようなコードを書くと、無限ループなっちゃうよ。

cont = nil
puts 'aaa'
callcc do |c|
  cont = c
  puts 'bbb'
  puts 'ccc'
end
puts 'ddd'
cont.call # 未来から過去を呼び出している!

実行結果だよ。

aaa
bbb
ccc
ddd
ddd
ddd
...

延々と「ddd」が出力され続けちゃうから、Ctrl+cで止めてね。

どうして無限ループなっちゃうのかと言うと、変数contが指している継続オブジェクトが「過去ジャンプする」という未来を持っているからだよ。だから、継続オブジェクト未来に「過去を呼び出す」動作を記述するときは、とても注意して記述しないとすぐに無限ループなっちゃうよ。

余談だけど、「CROSS†CHANNEL」というゲームをやったことがある人なら、「ループ世界」のようなものだと思えば理解しやすいかな?

じゃあ、次回は「過去の呼び出しをコントロールする方法」について勉強してみるよ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070603