2

現在、レイジー seq について学んでいますが、それらは通常、 を使用せずに再帰を伴うことに気付きましたrecur。たとえば、次の実装がありiterateます。

(defn iterate
  "Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects"
  {:added "1.0"
   :static true}
  [f x] (cons x (lazy-seq (iterate f (f x)))))

を使用せずに再帰するrecurと、呼び出しごとに新しいスタック フレームが作成され、十分な回数反復するとスタック オーバーフローが発生する可能性があるという印象を受けました。

lazy-seqスタックフレームをむさぼり食いますか? そうでない場合、どのようにそれを回避しますか?

4

2 に答える 2

4

lazy-seqこの例で iterate の呼び出しが含まれているのを見ると、実際には再帰的ではありません。むしろ、2 番目の項目を要求すると、lazy-seq マクロが中断された呼び出しに展開されます。したがって、アイテムを取得するたびに、もう一度呼び出しが発生しますが、古い呼び出しはスタックにありません。

また、メモリを消費しているかどうかも重要です。これは、シーケンスの先頭で何を行うかによって異なります。var または local バインディングで保持すると、コンス セルのチェーン全体が保持されます。ヘッドを保持することを避け、シーケンスをトラバースすると、GC が背後でクリーンアップできます。

1 つのトリッキーな点は、lazy-seq マクロに特別なメタ ^{:once true} が含まれていることです。これにより、fn クロージャーが 1 回限りのものであり、閉じられたオーバー変数を保持しないことをコンパイラーに知らせます。

于 2014-05-28T06:26:39.433 に答える
1

レイジー シーケンスは、スタック フレームをむさぼり食うことはありません。

このコードの表面では、再帰呼び出しのように見えるかもしれませんが、lazy-seq は関数ではなく、この特定のケースでは次のように変換されるマクロです。

(lazy-seq (iterate f (f x))

のようなものになります

(new clojure.lang.LazySeq (fn [] (iterate f (f x))))

ご覧のとおり、 iterate 関数呼び出しが別の関数内にラップされていることがわかります (つまり、関数は後で呼び出されます)。

于 2014-05-28T06:20:11.237 に答える