当你需要处理seq两次时,如何在不保留头部的情况下实现懒惰的seq?

Jam*_*yde 2 clojure

当一个函数给出一个很大的延迟seq时,避免保留头部是有益的,这样如果完全实现的序列不适合内存,你仍然可以处理它.例如,这工作正常:

(count (take 10000000 (range)))
(reduce + (take 10000000 (range)))
Run Code Online (Sandbox Code Playgroud)

但是这会产生内存不足错误:

(defn recount [coll n]
  [(count (take n coll))
   (reduce + (take n coll))])
(recount (range) 10000000)
Run Code Online (Sandbox Code Playgroud)

因为当计数实现了懒惰的seq时,coll的绑定会保留序列的头部.

我能想出的最接近的东西是一个宏,它强制重新评估seq而不是绑定:

(defmacro recount4 [coll n]
  `[(count (take ~n ~coll))
    (reduce + (take ~n ~coll))])
(recount4 (range) 10000000)
Run Code Online (Sandbox Code Playgroud)

这似乎没有广泛适用.

我查看了这个博客,但由于原子和可变状态的使用,解决方案不太令人满意.

Ale*_*ler 5

您可能希望查看eduction - 它会创建一个延迟的,非缓存的顺序集合,并将通过缩减重新评估每次使用的完整集合.