Clojure ленивое использование последовательности - PullRequest
42 голосов
/ 14 февраля 2011

Мне трудно понять, как создать ленивую последовательность в Clojure.

Документация для макроса мне не совсем понятна:

Использование: (lazy-seq & body) Принимает тело выражений, которое возвращает ISeq или nil, и возвращает объект Seqable, который будет вызывать тело только в первый раз вызывается, и кеширует результат и возвращает его на всех последующих последующие звонки.

Все примеры, которые я видел, выглядят примерно так:

; 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 находится за пределами вызова метода conc, как он предотвращает создание в бесконечной последовательности в ходе вычисления оператором coe?

Мой второй вопрос: всегда ли ленивые определения последовательности принимают эту общую форму?

1 Ответ

56 голосов
/ 14 февраля 2011

Вызов 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 - это лишь один из многих возможных способов создания ленивых последовательностей, и зачастую этоСамый удобный.Ниже приведены некоторые другие интересные / полезные способы создания ленивых последовательностей:

; 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)
...