Gauche > Archives > 2013/07/13

2013/07/13 08:22:20 UTCsmihica
#
はじめまして。青山といいます。gaucheの挙動についてご質問があるのですが、よろしければお答えいただければ。
2013/07/13 08:30:12 UTCshiro
#
どぞー。
2013/07/13 08:32:00 UTCsmihica
#
やばいすません途中で投稿してしまいまた。(let ((x 0) (c '())) (set! x (+ x (call/cc (lambda (c) (set! cc c) (c 1)) (if (< x 4) (cc 2) x)) をgaucheに打ち込むと、5が帰ってきますが、racketでは無限ループになります。これはどちらの挙動が正しいのでしょうか?継続の動作として考えると無限ループの方がただしそうですが、イマイチ分かりません。それとも未定義動作でしょうか?
#
あ、最初のletの中はcじゃなくてccですすみません
2013/07/13 08:39:48 UTCshiro
#
なんか括弧の数が合っていないような。エディタからcopy&pasteしてみてください。
2013/07/13 08:46:37 UTCsmihica
#
すいません。wifiかポート制限かかっていて、携帯からしか打てないんです。端末の表示がおかしくなってました。正しくは、(let ((x 0) (cc '())) (set! x (+ x (call/cc (lambda (c) (set! cc c) (c 1))))) (if (< x 4) (cc 2) x)) です。
2013/07/13 09:08:38 UTCshiro
#
それだと素直に考えれば5になるように思います。最初の(c 1)の呼び出しで(call/cc ...)から1が返って、xは最初0なので(+ 0 1)でxが1に。4より小さいので(cc 2)が呼ばれ、つまり(call/cc ...)から2が返って、xの値は(+ 1 2)で3に。まだ4より小さいので(cc 2)が呼ばれ、(call/cc...)から2が返って、xの値は(+ 3 2)で5に。そこで(< x 4)が偽になり、xの値である5が返る。
#
racketで無限ループになるのはなぜだろう。
2013/07/13 09:13:59 UTCsmihica
#
なるほど。そうなのですか、継続とは作られた時点でのスタックコピーを撮ってそこからやり直すものと聞いてましたので、無限ループの方が正しいのかなと思っていました。xのスコープが外にでているのであれば、boxingされるべきだと思うので、5になるのもわかるのですが。gaucheでは、set!の場合は常にheapに移動させるのでしょうか。
2013/07/13 09:33:40 UTCsmihica
#
とはいえ、普通に考えれば、gaucheの方がただしそうですね。ありがとうございました
2013/07/13 09:39:52 UTCsmihica
#
おーわかった、call/ccを左辺に持ってくると、racketでも5が帰りました。これxをスタックに積んだ状態でコピーしちゃうからですね。バグなのかな。goshはどちらも大丈夫という。すばらしいですね。
2013/07/13 09:41:33 UTCshiro
#
「スタックコピー」というのは概念的な話で、set!されるものについては状態変更が影響を与えます。xのスコープはcall/ccの外なのでboxingされると考えてもいいですよ。実装法としては、ほんとにスタックのコピー/書き戻しを行う&boxingする、という手もありますし、環境は一つだけ持ってみんなで共有するという手もあります(Gaucheの今の実装は継続が捕捉された時点で環境をヒープにコピーし、その環境を見てるコードは全てヒープに移された環境を参照するようになります。)
2013/07/13 09:48:02 UTCsmihica
#
なるほど。どうもありがとうございます!
2013/07/13 09:53:19 UTCsmihica
#
僕も3impを見て処理系を作っているところでしてとても参考になりました。ありがとうございました。僕の処理系も無限ループしてましたが、+のapply時にboxを解除するようにすれば動きそうです。それ以前に解除されて、スタックに生の値が乗ってるのがまずいみたいですね。多分。