Clojure 当您需要处理两次seq时,如何在不保留头部的情况下实现懒惰seq?
当一个函数被赋予一个大的延迟序列时,避免保留头部是有益的,这样在完全实现的序列不适合内存的情况下,您仍然可以处理它。例如,这很好:Clojure 当您需要处理两次seq时,如何在不保留头部的情况下实现懒惰seq?,clojure,Clojure,当一个函数被赋予一个大的延迟序列时,避免保留头部是有益的,这样在完全实现的序列不适合内存的情况下,您仍然可以处理它。例如,这很好: (count (take 10000000 (range))) (reduce + (take 10000000 (range))) 但这可能会产生内存不足错误: (defn recount [coll n] [(count (take n coll)) (reduce + (take n coll))]) (recount (range) 100000
(count (take 10000000 (range)))
(reduce + (take 10000000 (range)))
但这可能会产生内存不足错误:
(defn recount [coll n]
[(count (take n coll))
(reduce + (take n coll))])
(recount (range) 10000000)
因为coll的绑定在count实现惰性seq时保留了序列头
我能想到的最接近的东西是一个宏,它强制重新计算seq,而不是绑定:
(defmacro recount4 [coll n]
`[(count (take ~n ~coll))
(reduce + (take ~n ~coll))])
(recount4 (range) 10000000)
这似乎没有广泛的适用性
我看了一下,但由于使用了原子和可变态,解决方案不太令人满意 您可能需要查看-它创建了一个延迟的、非缓存的顺序集合,并将在每次使用时通过缩减重新评估完整集合。您需要的是
(defn recount5 [coll n]
(let [s (eduction (take n) coll)]
[(reduce (fn [r x] (inc r)) 0 s)
(reduce + s)]))
(recount5 (range) 10000000)
它允许对集合进行迭代或缩减。在大多数情况下,它的工作方式与集合类似,但没有实现底层的惰性seq
然而,count
对教育不起作用,因此我们必须重写count
作为一种减少
(defn recount5 [coll n]
(let [s (eduction (take n) coll)]
[(reduce (fn [r x] (inc r)) 0 s)
(reduce + s)]))
(recount5 (range) 10000000)
这确实有效。不过,我不得不用reduce重写count。