Clojure:超出gc开销限制,懒惰评估,pi序列

时间:2010-10-17 19:48:12

标签: clojure garbage-collection lisp jvm lazy-evaluation

下一个代码:

(ns clojure101.series)

(defn avg [[x y]] (/ (+ x y) 2))

(defn avg-damp
  [seq]
  (map avg (partition 2 seq)))

(defn avg-damp-n
  [n]
  (apply comp (repeat n avg-damp)))

(defn sums
  [seq]
  (reductions + seq))

(defn Gregory-Leibniz-n
  [n]
  (/ (Math/pow -1 n) (inc (* 2 n))))

(def Gregory-Leibniz-pi
     (map #(* 4 (Gregory-Leibniz-n %)) (iterate inc 0)))

(println (first ((avg-damp-n 10) (sums Gregory-Leibniz-pi))))

对于n = 20,我得到“超出gc开销限制”错误。 我该如何解决这个问题?

更新:我更改了avg-damp-n功能

(defn avg-damp-n
  [n seq]
  (if (= n 0) seq
      (recur (dec n) (avg-damp seq))))

现在我可以得到n = 20的数字

(time
 (let [n 20]
   (println n (first (avg-damp-n n (sums Gregory-Leibniz-pi))))))

20 3.141593197943081
"Elapsed time: 3705.821263 msecs"

更新2 我修正了一些错误,现在它运行得很好:

(ns clojure101.series)

(defn avg [[x y]] (/ (+ x y) 2))

(defn avg-damp
  [seq]
  (map avg (partition 2 1 seq)))

(defn avg-damp-n
  [n]
  (apply comp (repeat n avg-damp)))

(defn sums
  [seq]
  (reductions + seq))

(defn Gregory-Leibniz-n
  [n]
  (/ (int (Math/pow -1 n)) (inc (* 2 n))))

(def Gregory-Leibniz-pi
     (map #(* 4 (Gregory-Leibniz-n %)) (range)))

; π = 3.14159265358979323846264338327950288419716939937510...

(time
 (let [n 100]
   (println n (double (first ((avg-damp-n n) (sums Gregory-Leibniz-pi)))))))

输出:

100 3.141592653589793
"Elapsed time: 239.253227 msecs"

3 个答案:

答案 0 :(得分:3)

正如kotarak所说,在lazy seq上堆叠懒惰的seq似乎效率很低 关于GC。我可以在慢原子系统上重现这个问题。另见:

Error java.lang.OutOfMemoryError: GC overhead limit exceeded

对我来说,Gregory-Leibniz PI caclulation直接转换为仅使用一个懒惰序列的Clojure代码:

(defn Gregory-Leibniz-pi [n]
  (->> (range n)
       (map (fn [n] (/ (Math/pow -1 n) (inc (* 2 n)))))
       (apply +)
       (* 4)))

答案 1 :(得分:2)

首先,尝试一下有效的愚蠢解决方案:增加你的java堆空间。

;in your clojure launch script
java -Xmx2G ...other options...

程序的一部分在分区中并不是懒惰的,但更改它以使其变得懒惰(通过去除对count的调用)仍然给出了默认堆大小的OutOfMemoryError。用平均值的减少计算平均值取代avg-damp-n的聪明度

(take (integer-exponent 2 20) seq)

仍会导致OutOfMemoryError。查看其他所有内容的来源,我没有看到任何其他看起来应该消耗堆的东西。

答案 2 :(得分:2)

嗯......这对我有用。在Windows XP上使用Clojure 1.2进行测试。

user=> (defn avg
         [xs & {:keys [n] :or {n 2}}]
         (/ (reduce + xs) n))
#'user/avg
user=> (defn Gregory-Leibniz-n
         [n]
         (/ (Math/pow -1 n) (inc (+ n n))))
#'user/Gregory-Leibniz-n
user=> (->> (range)
         (map #(* 4 (Gregory-Leibniz-n %)))
         (reductions +)
         (partition 20)
         (map #(avg % :n 20))
         first
         println)
3.1689144018345354

这是正确的答案吗?我不知道这格雷戈里 - 莱布尼兹的递归,所以我 不确定这是否正确。

有一点我注意到:你想要太聪明了。即你的avg-damp-n 在懒惰的seq上堆叠懒惰的seq。由于您可以插入n的任意值, 您迟早会遇到大型n的堆栈溢出 这样的场景。如果有一个直截了当的解决方案,你应该更喜欢 它。不过,我不确定这是你的实际问题。 (正如我所说:相反 无益,它对我有用。)