How to convert lazy sequence to non-lazy in Clojure
ClojureLazy EvaluationClojure Problem Overview
I tried the following in Clojure, expecting to have the class of a non-lazy sequence returned:
(.getClass (doall (take 3 (repeatedly rand))))
However, this still returns clojure.lang.LazySeq
. My guess is that doall
does evaluate the entire sequence, but returns the original sequence as it's still useful for memoization.
So what is the idiomatic means of creating a non-lazy sequence from a lazy one?
Clojure Solutions
Solution 1 - Clojure
doall
is all you need. Just because the seq
has type LazySeq
doesn't mean it has pending evaluation. Lazy seq
s cache their results, so all you need to do is walk the lazy seq
once (as doall
does) in order to force it all, and thus render it non-lazy. seq
does not force the entire collection to be evaluated.
Solution 2 - Clojure
This is to some degree a question of taxonomy. a lazy sequence is just one type of sequence as is a list, vector or map. So the answer is of course "it depends on what type of non lazy sequence you want to get:
Take your pick from:
- an ex-lazy (fully evaluated) lazy sequence
(doall ... )
- a list for sequential access
(apply list (my-lazy-seq)) OR (into () ...)
- a vector for later random access
(vec (my-lazy-seq))
- a map or a set if you have some special purpose.
You can have whatever type of sequence most suites your needs.
Solution 3 - Clojure
This Rich guy seems to know his clojure and is absolutely right.
Buth I think this code-snippet, using your example, might be a useful complement to this question :
=> (realized? (take 3 (repeatedly rand)))
false
=> (realized? (doall (take 3 (repeatedly rand))))
true
Indeed type has not changed but realization has
Solution 4 - Clojure
I stumbled on this this blog post about doall
not being recursive. For that I found the first comment in the post did the trick. Something along the lines of:
(use 'clojure.walk)
(postwalk identity nested-lazy-thing)
I found this useful in a unit test where I wanted to force evaluation of some nested applications of map
to force an error condition.
Solution 5 - Clojure
(.getClass (into '() (take 3 (repeatedly rand))))