2011-11-10 12 views
3

私は、算術などの基本機能をSchemeの継続通過スタイルに変換する方法を理解しています。 しかし、関数に再帰が含まれる場合はどうなりますか?例えば 、スキーム - 継続通過スタイルへの変換

(define funname 
    (lambda (arg0 arg1) 
       (and (some procedure) 
         (funname (- arg0 1) arg1)))) 

は私にアドバイスをお願いします。 ありがとうございます。

答えて

1
(define (func x y k) 
    (some-procedure 
    (lambda (ret) 
    (if ret 
     (- x 1 
      (lambda (ret) 
       (func ret y k))) 
     (k #f)))) 

あなたは継続への唯一の明示的な呼び出しが(k #f)である理由であるベースケースを、欠けています。ベースケースがある場合は、ベースケースの戻り値も継承に渡します。たとえば:継続とCPSの良い説明があり

(define (func x y k) 
    (zero? x 
     (lambda (ret) 
      (if ret 
       (k y) 
       (some-procedure 
       (lambda (ret) 
        (if ret 
         (- x 1 
         (lambda (ret) 
          (func ret y k))) 
         (k #f)))))))) 
4

場所の一つは、KrishnamurthiのPLAI本です。関連するパート(VII)は本の他の部分に依存しないので、そこにすぐにジャンプすることができます。具体的には、手動でコードをCPSに変換し、再帰関数に取り組むという拡張例があります(第17章の第1部)。

さらに、私のクラスのためにこのテキストのextended versionを書きました。これは、より多くの例とその件に関する詳細があります。 PLAIのテキストに加えて、私はジェネレータの実装、あいまいな演算子のような継続の一般的な使用をカバーします。 (しかしPLAIは、私のテキストでカバーしていない実装戦略についての議論を続けています。)

1

これは、クリス・ジェスター・ヤングの答えを部分的に重複しています。

はCPSでは、あなたが探しているの違いは、(おおよそ)これら二つのものの間にある:

  • あなたは、プロシージャを呼び出すと、それをあなたが渡された継続を渡すことができます。これはダイレクトスタイルの最適化されたテールコールと同じです。
  • また、プロシージャを呼び出して、元の継続を引き継ぐ「戻り値」を持つ何かを行う新しいプロシージャを継続として渡すことができます。これはダイレクトスタイルのスタック呼び出しに相当します。

後者は、Chrisの例でラムダがやっていることです。基本的に、ラムダを評価するとクロージャが作成され、これらのクロージャは、ダイレクトスタイルのプログラムの実行時にスタックフレームが行うのと同じ仕事を行うために使用されます。スタックフレーム内の戻りアドレスの代わりに、クロージャーには継続機能のバインディングが含まれており、クロージャーのコードはこれを呼び出します。

関連する問題