如何在 Clojure 将惰性序列转换为非惰性序列

我在 Clojure 尝试了以下方法,希望返回非惰性序列的类:

(.getClass (doall (take 3 (repeatedly rand))))

但是,这仍然返回 clojure.lang.LazySeq。我的猜测是,doall确实计算了整个序列,但是返回了原始序列,因为它对制表仍然有用。

那么,从惰性序列创建非惰性序列的惯用方法是什么呢?

49534 次浏览

doall is all you need. Just because the seq has type LazySeq doesn't mean it has pending evaluation. Lazy seqs 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.

(.getClass (into '() (take 3 (repeatedly rand))))

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.

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

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.