0

スタックとヒープで何が起こっているのかを理解しようとしているプログラムが 3 つあります。すべてが無限ループ

1.

  (let ((f (lambda () 'ok))
        (g (lambda (a b) (a a b))))
           (g g f))

すべてのアプリケーションは末尾再帰です。スタックは問題ありません。2 つのラムダのみが作成されるため、ヒープは問題ありません。私は正しいですか?

2.

   (let ((f (lambda (a b)
               (a a (lambda () 'ok)))))
     (f f (lambda () 'ok)))

すべてのアプリケーションは末尾再帰です。スタックは問題ありません。ヒープについて: 無限のラムダ (lambda() 'ok)) が作成されます。私は正しいですか?-では、なぜメモリが終了しないのですか?

そして最後:

3.

   (let ((f (lambda (a b)
                (a a (lambda () (b))))))
       (f f (lambda () 'ok)))

2と3の違いは何ですか?なぜ2でメモリが終了したのですか? 私が正しくない場合、1回のループの後に3回:

 we activate this: (lambda (a b)  (a a (lambda () (b)))
 on                (lambda (a b)  (a a (lambda () (b)))
 and this          (lambda () 'ok)  (becuse this is (b)..)

これは 2 と同じです。

4

1 に答える 1

0

スマート コンパイラでは、ケース 1 とケース 2 は同等です。スマート コンパイラがなくても、ラムダが毎回新しく作成されるため、ケース 2 では無限の数のラムダが作成される可能性がありますが、それらは即座にガベージ コレクション可能です。したがって、結果は同じで、一定のスタックとヒープの使用量になります。

ケース 3 では、それぞれの新しいラムダには前のラムダへの自由な参照が含まれているため、作成されるラムダはいずれもガベージ コレクション可能ではありません。スタックの使用量は一定のままですが、ヒープの使用量は膨れ上がります。(本当に賢いコンパイラは、ラムダが実際には使用されていないことを把握し、それを完全に排除することができますが、自由参照に関する私の一般的なポイントは当てはまります。)

コンパイラは、それ(lambda () (b))がすでに nullary(-only) プロシージャであることを証明できる場合、および/または結果のラムダが nullary 形式でのみ呼び出されることを証明できる場合 (そうでない場合は、型を追加し、アリティ チェッカー)。繰り返しますが、それをあなたの例に適用するには、かなり賢いコンパイラが必要です。bb

于 2013-02-04T13:16:40.470 に答える