web-dev-qa-db-ja.com

Clojureレイジーシーケンスの使用法

Clojureでレイジーシーケンスを作成する方法を理解するのに苦労しています。

マクロのドキュメントは私にはまったくわかりません。

使用法:(lazy-seq&body)ISeqまたはnilを返す式の本体を取り、seqが最初に呼び出されたときにのみ本体を呼び出し、結果をキャッシュして後続のすべてに返すSeqableオブジェクトを生成します。 seq呼び出し。

私が見たすべての例は、次のようなことをしているようです。

; return everything in the sequence starting at idx n
(defn myseq-after-n [n]
  (...)
)

(def my-lazy-seq
  (lazy-seq (conj [init-value] (myseq-after-n 2)))
)

だから、私が最初に得られないのは、lazy-seqがconjの呼び出しの外にあるので、conjが評価時に無限のシーケンスを生成するのをどのように防ぐのですか?

私の2番目の質問は、怠惰なシーケンス定義は常にこの一般的な形式を取るのでしょうか?

43
Kricket

Lazy-seq呼び出しは、最初にアクセスされたときに本体を実行し、その後キャッシュして、後で再度呼び出されるたびに同じ結果を返します。

これを使用して長い(または無限の)シーケンスを構築する場合は、返されたシーケンスに他のlazy-seq呼び出しを再帰的にネストする必要があります。これが私が考えることができる最も単純なケースについてです:

(defn ints-from [n]
  (cons n (lazy-seq (ints-from (inc n)))))

(take 10 (ints-from 7))
=> (7 8 9 10 11 12 13 14 15 16)

(ints-from n)呼び出しは、nで始まり、その後に(ints-from(inc n))の遅延シーケンスが続くシーケンスを生成します。これは無限のリストですが、lazy-seqにより、(int-from(inc n))が必要な場合にのみ呼び出されることが保証されるため、問題はありません。 lazy-seqを使用せずにまったく同じコードを試すと、StackOverflowErrorがすぐに発生します。

lazy-seqは、レイジーシーケンスを作成するための多くの可能な方法の1つにすぎず、多くの場合、最も便利ではありません。以下は、怠惰なシーケンスを作成するための他のいくつかの興味深い/便利な方法です。

; range is an easy way to get an infinite lazy sequence of integers, starting with zero     
(take 10 (range))
=> (0 1 2 3 4 5 6 7 8 9)

; map produces lazy sequences, so the following is lazy 
(take 10 (map #(* % %) (range)))
=> (0 1 4 9 16 25 36 49 64 81)

; iterate is a good way of making infinite sequenes of the form x, f(x), f(f(x))..... 
(take 10 (iterate (partial * 2) 1))
=> (1 2 4 8 16 32 64 128 256 512)
56
mikera