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。