4

次の2つのコードの違いが気になります:

(define cont2 #f) 
(call/cc (lambda (k) (set! cont2 k)))
(display "*")
(cont2 #f)

(let [(cont #f)]
  (call/cc (lambda (k) (set! cont k)))
  (display "*")
  (cont #f))

私の意見では、これら 2 つのプログラムの正しい動作は、'*' を無限に出力することです。ただし、最初のものは '*' を 1 つだけ出力して終了しますが、2 番目のものは正しい動作を示します。

だから私は混乱しています。何か特別なことをされているのでしょうdefine か、それとも続きが思っていたものと違っていたのでしょうか。

別の推測では、環境の最上位は次のように特別に扱われます。

(define (test)
  (define cont2 #f) 
  (call/cc (lambda (k) (set! cont2 k)))
  (display "*")
  (cont2 #f))
(test)

これは機能しますが、なぜですか?

ご協力ありがとうございました!

4

4 に答える 4

8

Racket では、各トップレベルの式はプロンプトでラップされます。

call/cc最初の例では、「現在の継続を最も近いプロンプトまでキャプチャする」だけなので、他のトップレベルの式はキャプチャされないため、に適用cont2する#f#f.

また、最初の例を でラップしても、トップレベルの式であるかのように、トップレベルがその内容を暗黙的にスプライスbeginするため、状況は変わりません。begin

于 2013-08-07T06:41:25.337 に答える
4

あなたがトップレベルにいるとき、(プロンプト文字 '>' に注意してください) の続き:

> (call/cc (lambda (k) (set! cont2 k)))

トップレベルの read-eval-print-loop です。つまり、最初のコード スニペットでは、式を 1 つずつ入力し、それぞれの後にトップレベルに戻ります。代わりにあなたがした場合:

(begin
  (define cont3 #f)
  ...
  (cont3 #f))

無限の '*' を取得します ( の完了時にのみトップレベルに戻るためbegin)。3 番目のコード スニペットはこれのインスタンスです。継続はトップレベルのループではないため、無限の '*' が得られます。

于 2013-08-07T05:17:28.143 に答える
2

それはあなたの意見だけではありません。R5RS同じ動作をしない一方または両方でこれを行うR6RSと、レポートに違反します。(r5rs実装)ではracket、私がテストしたのでおそらく違反してplt-r5rsおり、明らかに永遠にループしません。

#lang racket(実装のデフォルト言語racket) はどの標準にも準拠していないため、 のようperl5に、どのように動作するかは仕様です。彼らのドキュメントには、継続の範囲を縮小するプロンプトタグについて多くのことが書かれています。

この質問を読んで頭に浮かぶcall/cc に対する議論があります。興味深い部分は次のとおりです。

とにかく、実際のSchemeシステムに実装されている call/cc は継続全体をキャプチャしないことを示しています.多くのSchemeシステムは、REPLまたはスレッドの周りに暗黙の制御区切り文字を置きます. これらの暗黙的な区切り文字はすぐにわかります。たとえば、Petite Chez または Scheme48 では、コード

 (let ((k0 #f))
   (call/cc (lambda (k) (set! k0 k)))
   (display 0)
   (k0 #f))

終わりのないゼロのストリームを出力します。各操作を独自の行に配置すると (独自の REPL によって評価されます):

 (define k0 #f)
 (call/cc (lambda (k) (set! k0 k)))
 (display 0)
 (k0 #f)

出力は単なる 0 #f です。

私がcall/ccプリミティブとして反対しているかどうかはわかりません (あなたのツールは、自分の足を撃つ可能性を与えるべきだと思います)。ただし、Scheme コンパイラを作成した後は気が変わるかもしれないので、気が向いたときに戻ってきます。

于 2013-08-07T21:25:26.333 に答える
0

これも機能します。理由は聞かないでください。(call/cc は私の心を吹き飛ばします)

(define cont2 #f)     
((lambda () 
  (call/cc (lambda (k) (set! cont2 k)))
  (display "*")
  (cont2 #f)))

テスト定義では、3 つの行は、トップ レベルで呼び出される一緒に呼び出す暗黙の begin 構造内にあります。私のバージョンでは、自分自身を呼び出す匿名のサンクを作成しました。cont2 の最初の定義では、定義は値のプレースホルダーを作成するだけであり、その後の関数呼び出しは環境内でリンクされていません。

于 2013-08-07T05:13:00.253 に答える