24

Kent Dybvigによるスキームプログラミング言語(第4版)のセクション3.4で、彼は継続渡しスタイルとは何かを非常に明確に説明しています。彼が2つ の理由を挙げている理由は次のとおりです。

  1. 継続を実装するプロシージャは任意の数の引数を取ることができるため、複数の結果を継続に渡します。
  2. CPSでは、プロシージャが個別の継続を取得することもできます...これは異なる数の引数を受け入れる場合があります。

最初の理由はvalues手順を使用して実行でき、2番目の理由はを使用して実行できるためcase-lambda、継続渡しスタイルを使用する利点がわかりません。誰かが、継続渡しスタイルが適切である場所、コードをより良く、より明確にするなどの例をいくつか教えてもらえますか?

4

2 に答える 2

15

Dybvigは、このセクションの明示的な継続を使用してcall/cc、言語の一部として持つことを動機付けます。重要な点は、セクションの終わり近くで、それなしでコードを書くには、呼び出す関数を含め、使用されるすべてのコードのグローバルな変換が必要であると述べたときです。したがって、Schemeでは通常、マクロを使用して独自の構成を作成します。継続はこれらの便利な構成の1つですが、ローカル変換のみを実装するため、マクロを介して実装することはできません。

ただし、CPSスタイルを直接使用することは、依然として有用です。たとえば、彼が述べているように、単一入力関数を受け取って送信する解析関数のように、複数の継続性を持つ関数を作成できます。値を解析し、解析が失敗したときに呼び出すnullary失敗関数(この関数はエラーまたはバックトラックで中止され、他の解析ルールを使用しようとする場合があります)。call/ccもう1つの可能な使用法は、完全なコンテキストを取得するのではなく、継続に入る内容を正確に制御したい場合です。

ファーストクラスの継続性がない言語でコードを書くという明らかなケースもあり、CPSedコードを唯一の選択肢にします。その一例は、IOを使用し、明示的なCPSでコードを記述しなければならない多くのnode.jsプログラムです。

于 2011-12-17T15:38:09.937 に答える
8

最初の理由はvaluesプロシージャを使用して実行でき、2番目の理由はcase-lambdaを使用して実行できるため、継続渡しスタイルを使用する利点がわかりません。

values...の定義が、複数の引数で継続を呼び出すことを指定していることを除いて。

継続渡しスタイルが役立つ問題の私のお気に入りの例は、パターンマッチャーを書くことです。caseこれはステロイドのようなマクロの一種です。値を取り、その構造を、ペア、シンボル(変数を表す)、および非シンボルアトム(値を表す)から構築された一連のパターンと照合しようとします。一致が成功すると、パターン内の識別子が値の対応するサブパートにバインドされ、そのパターンの結果が実行されます。失敗した場合は、次のパターンを試行します。

この種のマクロを継続渡しスタイルの形式で記述するのは非常に簡単です。2つの異なる継続を使用して、「一致が成功した場合の対処方法」(成功の継続)と「一致が失敗した場合の対処方法」(失敗の場合)を表します。継続)。

私がかつて書いたパターンマッチングマクロのこの(簡略化された)フラグメントを取り上げます(構文ケースまたは構文ルールがわからない場合はお詫びします。その場で適応させたので、うまくいくことを願っています!)。ペアパターンに一致するルールに焦点を当てます。これは、ヘッドパターンとテールパターンの2つのパターンのペアで構成されるパターンです。頭が頭のパターンと一致し、尾が尾のパターンと一致するペアと一致します。

;;;
;;; Outer "driver" macro; the meat is in pmatch-expand-pattern.
;;;
(define-syntax pmatch
  (syntax-rules ()
    ((pmatch value-expr (pattern . exprs) . clauses)
     (let* ((value value-expr)
            (try-next-clause
             (lambda () (pmatch value . clauses))))
       (pmatch-expand-pattern pattern
                              value
                              ;; success-k
                              (begin . exprs)
                              ;; failure-k
                              (try-next-clause))))))

(define-syntax pmatch-expand-pattern
  (lambda (stx)
    (syntax-case stx ()

      ;; Cases for constants and quoted symbols omitted, but they're trivial.

      ;; Match a pair pattern.  Note that failure-k is expanded three times; 
      ;; that's why pmatch encapsulates its expansion inside a thunk!
      ((pmatch-expand-pattern (head-pat . tail-pat) value success-k failure-k)
       (syntax
        (if (pair? value)
            (pmatch-expand-pattern head-pat 
                                   (car value)
                                   ;; If we successfully match the head, then
                                   ;; the success continuation is a recursive
                                   ;; attempt to match the tail...
                                   (pmatch-expand-pattern tail-pat
                                                          (cdr value)
                                                          success-k 
                                                          failure-k)
                                   failure-k))
            failure-k))

      ;; Match an identifier pattern.  Always succeeds, binds identifier
      ;; to value
      ((pmatch-expand-pattern identifier value success-k failure-k)
       (identifier? (syntax identifier))
       (syntax (let ((identifier value)) success-k)))
      )))

pmatch-expand-patternマクロ式のsuccess-kおよびfailure-kサブフォームに注意してください。これらは、パターンマッチャーの「継続」として、少し緩い用語で使用されている式を表します。成功の継続は、検討中のパターンが検討中の値と一致する場合に使用されます。失敗の継続は、そうでない場合に使用されます。成功の継続は、現在の最上位パターンのすべてにまだ一致したかどうかに応じて、パターンの残りの部分に一致する式か、パターンの一致が完了したときに実行する結果のいずれかになります。失敗の継続は、パターンが一致しない場合に、次の選択ポイントに戻るために使用されます。

前述したように、「継続」という用語は、を継続として使用しているため、上記のコードでは少し緩く使用されています。ただし、これは、これをマクロとして実装する方法の詳細にすぎません。アルゴリズムは、実際の手順を継続して、実行時に純粋に実装できます。(また、try-next-clause手順は文字通りの意味での継続です。)

于 2011-12-18T01:43:46.890 に答える