Clojure:减少大型懒惰收集会占用内存

ora*_*ock 1 memory performance clojure sequence lazy-evaluation

我是Clojure的新手.我有以下代码,它创建了一个无限懒惰的数字序列:

(defn generator [seed factor]
  (drop 1 (reductions 
            (fn [acc _] (mod (* acc factor) 2147483647))
            seed 
            ; using dummy infinite seq to keep the reductions going
            (repeat 1))))
Run Code Online (Sandbox Code Playgroud)

序列中的每个数字都取决于先前的计算.我正在使用,reductions因为我需要所有中间结果.

然后我实例化两个生成器,如下所示:

(def gen-a (generator 59 16807))
(def gen-b (generator 393 48271))
Run Code Online (Sandbox Code Playgroud)

然后我想比较n这些序列的连续结果,对于大n,并返回它们相等的次数.

起初我做了类似的事情:

(defn run []
  (->> (interleave gen-a gen-b)
       (partition 2)
       (take 40000000)
       (filter #(apply = %))
       (count)))
Run Code Online (Sandbox Code Playgroud)

这花了太长时间,我看到该程序的内存使用量飙升至约4GB.有些printlns我看到它经过大约1000万次迭代后变得非常慢,所以我想可能count需要将整个序列存储在内存中,所以我把它更改为使用reduce:

(defn run-2 []
  (reduce
    (fn [acc [a b]]
      (if (= a b)
        (inc acc)
        acc))
    0
    (take 40000000 (partition 2 (interleave gen-a gen-b)))))
Run Code Online (Sandbox Code Playgroud)

尽管如此,它还是分配了大量内存并在前几千万之后显着放缓.我很确定它将整个懒惰序列存储在内存中,但我不知道为什么,所以我试图手动扔掉头部:

(defn run-3 []
  (loop [xs (take 40000000 (partition 2 (interleave gen-a gen-b)))
         total 0]
    (cond
      (empty? xs) total
      (apply = (first xs)) (recur (rest xs) (inc total))
      :else (recur (rest xs) total))))
Run Code Online (Sandbox Code Playgroud)

同样,结果相同.这让我很难过,因为我读到我用来创建xs序列的所有函数都是懒惰的,因为我只使用当前项目,所以我期望它使用常量内存.

来自Python背景我基本上试图模拟Python生成器.我可能错过了一些明显的东西,所以我真的很感激一些指针.谢谢!

Joo*_*aat 6

生成器不是(懒惰)序列.

你在这里坚持:

(def gen-a (generator 59 16807))
(def gen-b (generator 393 48271))
Run Code Online (Sandbox Code Playgroud)

gen-a并且gen-b是gobal vars指的是头部序列.

你可能想要这样的东西:

(defn run []
  (->> (interleave (generator 59 16807) (generator 393 48271))
       (partition 2)
       (take 40000000)
       (filter #(apply = %))
       (count)))
Run Code Online (Sandbox Code Playgroud)

或者,定义gen-agen-b作为功​​能:

(defn gen-a
  []
  (generator 59 16807)))
...

(defn run []
  (->> (interleave (gen-a) (gen-b))
       (partition 2)
       (take 40000000)
       (filter #(apply = %))
       (count)))
Run Code Online (Sandbox Code Playgroud)