Reading — step 1 of 5
Learn
~1 min readSTM and Lazy Sequences
Most Clojure sequence operations are lazy — they don't compute until consumed. This lets you work with infinite sequences and avoid unnecessary work.
(def naturals (iterate inc 1)) ;; (1 2 3 4 5 ...) — infinite!
(take 5 naturals) ;; (1 2 3 4 5)
(take 5 (map #(* % %) naturals)) ;; (1 4 9 16 25)
(first (drop 1000000 naturals)) ;; 1000001 — only computes 1M+1 elements
Generators with lazy-seq:
(defn primes
([] (primes 2))
([n] (lazy-seq
(cons n (filter #(not= 0 (mod % n)) (primes (inc n)))))))
(take 10 (primes)) ;; first 10 primes
The lazy-seq macro defers the body's evaluation until the sequence is consumed. The recursive call doesn't blow the stack — each step is computed on demand.
Realization — when does laziness end?
doall— force the whole seqdorun— like doall but discards results (for side effects)take,drop,nth,count— partial realizationfirst,rest,next— peek without realizing more than needed
Pitfalls:
- Holding the head: keeping a reference to a lazy seq's first element prevents GC of the rest. Memory leak.
- Infinite seqs in
count— never returns. - Side effects in
mapbody — only runs when consumed. Usedoseqormapvfor eager.
iterate, repeatedly, range, cycle — common lazy generators:
(take 5 (iterate #(* 2 %) 1)) ;; (1 2 4 8 16)
(take 3 (repeatedly #(rand))) ;; 3 random numbers
(range 1 10 2) ;; (1 3 5 7 9)
(take 6 (cycle [:a :b :c])) ;; (:a :b :c :a :b :c)
Streams over big files:
(with-open [r (clojure.java.io/reader "big.log")]
(->> (line-seq r)
(filter #(re-find #"ERROR" %))
(take 100)
(doall))) ;; force realization while reader still open
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…