5

トップレベルなどの構成を使用せずに、スキームプロシージャで「アーリーリターン」を実行する方法を見つけようとしています。ifcond

(define (win b)
 (let* ((test (first (first b)))
        (result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
                       (enumerate (length b)))))
  (when (and (not (= test 0)) result) test))
 0)

たとえば、上記のコードでは、条件が満たされた場合は戻り、そうでない場合は0を返します。ただし、条件の結果に関係なく、プロシージャはwin常に0を返しますtestwhenwhen

私がこのようにコードを構造化する理由は、この手順では多数の複雑なチェック(let*例のように複数のブロック)を実行する必要があり、すべてを大きなものに入れるのcondは非常に扱いにくいためです。

4

4 に答える 4

7

call/ccを使用してreturn自分自身を構築する方法は次のとおりです。

(define (example x)
  (call/cc (lambda (return)
    (when (< x 0) (return #f))
    ; more code, including possible more calls to return
    0)))

一部のスキームでは、ラムダのノイズの一部をドロップできるlet/ccというマクロを定義しています。

(define (example x)
  (let/cc return
    (when (< x 0) (return #f))
    0))

もちろん、Schemeがそうでない場合は、let/ccを書くのは簡単です。


これは、call/ccが継続として呼び出されたポイントを保存するために機能します。その継続を関数の引数に渡します。関数がその継続を呼び出すと、Schemeはこれまでに構築した呼び出しスタックをすべて破棄し、call/cc呼び出しの最後から継続します。もちろん、関数が継続を呼び出さない場合は、通常どおりに戻ります。

継続は、その関数から継続を返すか、グローバルデータ構造に格納して後で呼び出すまで、本当に気が遠くなることはありません。それ以外の点では、他の言語の構造化されたgotoステートメントとまったく同じです(while / for / break / return / continue / exceptions / conditions)。


完全なコードがどのように見えるかはわかりませんが、condを使用して、複雑なチェックを別々の関数に分解する方がよい場合があります。必要returnlet*あり、通常は過度に命令型のコードの症状です。ただし、call / ccメソッドを使用すると、今のところコードが機能するはずです。

于 2010-03-12T18:07:47.040 に答える
1

1つの方法は、ループの代わりに再帰を使用することです。その後、それ以上再帰しないことで早期終了が実現されます。

于 2010-03-12T16:53:41.703 に答える
0

「現在の継続による呼び出し」サポートを使用して、リターンをシミュレートできます。ウィキペディアに例があります。この関数はcall-with-current-continuationと呼ばれますが、まったく同じものであるcall/ccと呼ばれるエイリアスがよくあります。ここにも少しわかりやすい例があります

注:これは非常に高度なSchemeプログラミング手法であり、最初は少し気が遠くなる可能性があります... !!!!

于 2010-03-12T16:58:34.440 に答える
0

この場合、トップレベルではありませんが、whenは必要ありません。ifが必要です。

(define (win b)
  (let* ((test (first (first b)))
         (result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
                        (enumerate (length b)))))
    (if (and (not (= test 0)) result) 
        test
        0)))

常にゼロを返していたのは、実行されたときに本体が実行されたかどうかに関係なく、その結果が床にドロップされるためです。ご覧のとおり、関数定義フォームに暗黙的に含まれるラムダは、暗黙的な開始ブロックも作成します。

(define foo 
  (lambda (b)
     (begin
       (let ...)
       0)))

そして、beginが機能する方法は、すべての中間結果をフロアにドロップしながら、内部の最後のフォームの結果を返すことです。これらの中間結果は、副作用をもたらすことを目的としています。あなたはそれのどれも使っていません、それは素晴らしいです(!)、しかしあなたは関数定義の中に1つのフォーム(あなたが本当に望む結果)だけを持つように注意する必要があります。

グレム

于 2010-03-12T22:04:05.473 に答える