5

怠惰な作業とキャッシュがどのように機能しているかを理解するのに非常に苦労しています。

ここでは、lazy-seq が機能している段階的な例が本当に役立つと思います。たとえば、次の質問を読みました。

Clojure 遅延シーケンスの使用法

しかし、それはまだ明らかではありません。

私の質問は、別の呼び出しがキャッシュされた呼び出しと「等しい」かどうかを呼び出しがどのように判断し、キャッシュにどれくらいの期間留まるかということです。(source lazy-seq)しようとしましたが、明らかにそれは Java の土地にあるため、ここでは運が悪いです。

引数を 1 つだけ取る単純なレイジー seq (2 の累乗のリストなど) を 5 と 8 で呼び出すとどうなるでしょうか? この 2 つの値だけがキャッシュされますか?

そして、すでに遅延関数を呼び出したすべての入力をキャッシュしてメモリを台無しにする場合、無限の構造を取得するために無限リストを作成してキャッシュするポイントは何ですか?

後続のすべての呼び出しで結果をキャッシュしていると言っているため...「s」を使用します。

1: 引数の結果が '1' キャッシュされます 2: 引数の結果が '2' キャッシュされます 3: 引数の結果が '3' キャッシュされます ... 2 30: 2 30まで数えました。すべて、しかし今ではメモリ内に 2**30 のキャッシュがあり、後続のすべての呼び出しのために以前のすべての呼び出しをキャッシュしています。

それとも、キャッシュされたのは最後の呼び出しですか?

木を引数として取る遅延関数を書いたらどうなるでしょうか? それは等しく実行されますか?新しい評価が必要かどうかを知るために渡された引数について?

この動作は、実行時に何らかの形で追跡できますか?

4

2 に答える 2

8

遅延シーケンスの「キャッシュ」は、webapp で使用するように有効期限が切れる変更可能なキャッシュではありません。サイズが 1 のキャッシュであり、リストの各セルに 1 つあります。その「キャッシュ」には、値または値を計算するコードのいずれかが含まれ、両方が含まれることはありません。値を計算すると、その値を (そのセル/エントリに) キャッシュし、誰かがセルを再度読み取ると、コードを呼び出す代わりに直接値を与えます。

これは、ポイントを説明するための単純化された架空の repl セッションです。

user> (def a (range))
a = [code-for-rest]
user> (first a)
a = [code-for-first, code-for-rest]
a = [0, code-for-rest]
result=> 0
user> (first a)
a = [0, code-for-rest]
result=> 0
user> (nth a 10)
a = [0]->[1]->[2]->[3]->[4]->[5]->[6]->[7]->[8]->[9, code-for-rest]
result=> 4

この例では、各セルには最初に値を生成するコードとリストの残りを生成するコード (またはこれがリストの最後である場合は nil) が含まれています (これはこの点を説明するために単純化したものです)。そのセルが認識されると (非遅延になると)、その内容が実際の値に置き換えられるため、値と残りのシーケンスを生成するためのコードが含まれるようになります。リスト内の次のセルが読み取られると、最初に (セルに含まれているように) 残りのコードによって生成され、次に新しいセルの n 番目のコードがそのセルの値を生成します。

于 2012-12-05T23:04:06.543 に答える
1

ここに、実行時に何が起こっているかを示すおもちゃの例があります。

(defn times-two[number]
 (print "- ")
 (* 2 number))

(def powers-of-two (lazy-cat [1 2] (map times-two (rest powers-of-two))))

(println (take 10 powers-of-two))
(println (take 12 powers-of-two))

出力は次のようになります。

(1 - 2 - 4 - 8 - 16 - 32 - 64 - 128 - 256 512)

(1 2 4 8 16 32 64 128 256 - 512 - 1024 2048)

于 2012-12-06T02:22:59.663 に答える