2017-12-05 6 views
0

combineは、バイナリ演算子binでリストを減らす必要がありますが、述語pred?に失敗した値を検出した場合はexcを返し、その値を検出した場合はリストで計算を実行してはなりません。Racketでのcall/ccの面白い動作を説明するには?

これは簡単な問題です。

#lang racket 

(define (id x) 
    x) 

(define (const x) 
    (lambda (_) x)) 

(define (combine pred? bin zero exc) 
    (call/cc 
    (lambda (exit) 
    (letrec 
     ((f (lambda (xs) 
       (if (empty? xs) 
        zero 
        (if (pred? (first xs)) 
         (exit exc) 
         (bin (first xs) (f (rest xs)))))))) 
     f)))) 

(define product 
    (combine zero? 
      * 
      1 
      0)) 

(product '(1 2 3 0 4)) 

コードはほぼ動作しますが、それは非常に微妙な誤差を持っています。

これは、次の例外が発生します:

define-values: assignment disallowed; 
cannot re-define a constant 
    constant: product 

それを動作させるのは簡単です。わずかな変更が必要とされている:

(define (combine pred? bin un zero exc) 
    (lambda (ys) 
    (call/cc 
    (lambda (exit) 
     (letrec 
      ((f (lambda (xs) 
       (if (empty? xs) 
        zero 
        (if (pred? (first xs)) 
         (exit exc) 
         (bin (first xs) (f (rest xs)))))))) 
     (f ys)))))) 

私はこの問題は、ラケットは、定義の継続は関数を定義することですが、誰もがより多くの詳細を与えることができることであることを参照してください?たとえば、どのような構文的変換が関与していますか?あなたがここに持っている必要がありますための

答えて

1

思う:

(define product (combine ....)) 

ここでは、最初(combine ...)を評価し、継続が名前productとして計算値を格納始まるプログラムの残りの部分です。 fからの継続を呼び出すと、すぐにproductの記憶に戻る。

(product '(1 2 3 0 4))を呼び出すと、productがプログラムの続きであり、call/ccが継続をキャプチャしているため、再定義しています。

2回目の試みでは、ラムダでそれをラップし、同じ引数でfを呼び出すというリファクタリングを行います。それはちょうどcall/ccが起こるときに遅れているので、継続に何が含まれていますか?このバージョンでは、継続は(product ...)の後に来るもので、設定はしません。product(define product ...)

+0

これはスキーム自体の面で説明できますか、実装の中で言語を見なければなりませんか? –

+0

@Rdgstv '#lang racket'は標準に準拠していませんが、互換性があるようです(http://www.r6rs.org/final/html/r6rs/r6rs-ZH-14.html#node_idx_352)実行の責任:実装は、の継続が2回以上呼び出されたことを検出しなければなりません。実装がこれを検出した場合、条件タイプ&アサーションで例外を発生させる必要があります。ラケット(実装)は、標準のSchemeレポート言語の多くをサポートしています。私は同じプリミティブとして 'define'と' set! 'を持つ多くの "scheme"実装を見てきました。 – Sylwester

+0

@Rdgstv 'call/cc'について理解しています。すべての 'call/cc'は、継続を関数として公開することです。多くのSchemeの実装は、とにかくコードをCPSに変えますが、 'call/cc'ではそれをしません。 CPS 'call/cc'はちょうど'(define(call/cc body c)(body(ラムダ(値の実際の継続)(c値)))c)) 'です。 'define 'のような特別な形式はCPSではできません。関数内からトップレベルのバインディングを定義するプリミティブがないからです。 (関数内の 'define'は' letrec'となり、これは私たちが望むものではありません) – Sylwester

関連する問題