user333265
user333265

Reputation: 13

Clojure. Why the wrapper lazy-seq is required?

Why the wrapper lazy-cons is required? There are two functions with the same result.

(defn seq1 [s]
  (lazy-seq
    (when-let [x (seq s)]
      (cons (first x) (seq1 (rest x))))))


(defn seq2 [s]
    (when-let [x (seq s)]
      (cons (first x) (seq2 (rest x)))))

Both case I got the same result none chunked sequences.

repl.core=> (first (map println (seq1 (range 1000))))
0
nil
repl.core=> (first (map println (seq2 (range 1000))))
0
nil
repl.core=> (chunked-seq? (seq2 (range 1000)))
false
repl.core=> (chunked-seq? (seq1 (range 1000)))
false

Upvotes: 1

Views: 83

Answers (1)

Carcigenicate
Carcigenicate

Reputation: 45740

The first is lazy. It only evaluates elements of the sequence as needed. The second however is strict and goes through the entire sequence immediately. This can be seen if you add some println calls in each:

(defn seq1 [s]
  (lazy-seq
    (when-let [x (seq s)]
      (println "Seq1" (first x))
      (cons (first x) (seq1 (rest x))))))


(defn seq2 [s]
  (when-let [x (seq s)]
    (println "Seq2" (first x))
    (cons (first x) (seq2 (rest x)))))

(->> (range 10)
     (seq1)
     (take 5))
Seq1 0
Seq1 1
Seq1 2
Seq1 3
Seq1 4 ; Only iterated over what was asked for
=> (0 1 2 3 4) 

(->> (range 10)
     (seq2)
     (take 5))
Seq2 0
Seq2 1
Seq2 2
Seq2 3
Seq2 4
Seq2 5
Seq2 6
Seq2 7
Seq2 8
Seq2 9 ; Iterated over everything immediately
=> (0 1 2 3 4) 

So, to answer the question, lazy-seq is only required if you intend the iteration of the sequence to only happen as needed. Prefer lazy solutions if you think you may not need the entire sequence, or the sequence is infinite, or you want to sequence several transformations using map or filter. Use a strict solution if you're likely going to need the entire sequence at some point and/or you need fast random access.

Upvotes: 2

Related Questions