我正在Clojure中实现一个简单的算法,即使在我的:jvm-opts ["-Xmx4G"]
上设置了project.clj
,它也会一直在摧毁内存。假设以下数据结构:
(def inf Double/POSITIVE_INFINITY)
(def min-dist (atom {:1 {:1 0 :2 4} :2 {:1 4 :2 0 :3 5} :3 {:2 5 :3 0}}))
(def vertexes [:1 :2 :3])
以下内容将在较大的输入(|vertexes| = 100
)上耗尽:
(for [k vertexes i vertexes j vertexes]
(do
(println " " i " " j " "k)
(let [s (+ (get-in @min-dist [i k] inf) (get-in @min-dist [k j] inf))]
(if (> (get-in @min-dist [i j] inf) s) (swap! min-dist assoc-in [i j] s)))))
输出:
OutOfMemoryError Java heap space java.util.Arrays.copyOf (Arrays.java:2367)
我很确定这是一个reduce
选项,可以让所有东西干净而快速,但我找不到它。看起来swap!
占用了大量的内存空间,我是对的吗?
两个奖金问题:
如果我删除println
行(当然还有do),代码将快速运行,但min-dist
将不会更新,就好像循环不是执行。为什么?
使用lein run
运行时会看到相同的行为,即使其中有println
。为什么呢?
对新Clojurist的任何帮助将不胜感激。 =)
答案 0 :(得分:9)
你的子问题#1是关键。
for
生成 lazy 列表,因此除非实际读取结果,否则不会执行任何工作。如果你想要对结果进行评估,你可以将整个事件包装在dorun
的调用中,该调用遍历列表而不会将整个内容保留在内存中。我将这种情况称为“被懒惰的小虫咬伤”,大多数Clojurians发生的事情比他们更容易发生; - )
user> @min-dist
{:1 {:1 0, :2 4}, :2 {:1 4, :2 0, :3 5}, :3 {:2 5, :3 0}}
user> (time
(dorun (for [k vertexes i vertexes j vertexes]
(let [s (+ (get-in @min-dist [i k] inf) (get-in @min-dist [k j] inf))]
(if (> (get-in @min-dist [i j] inf) s) (swap! min-dist assoc-in [i j] s))))))
"Elapsed time: 4.272336 msecs"
nil
user> @min-dist
{:1 {:1 0, :2 4, :3 9}, :2 {:1 4, :2 0, :3 5}, :3 {:2 5, :3 0, :1 9}}
删除println是一个好主意,因为有一个100个顶点用于表达的列表(它不是在其他语言中有词的意义上的循环)将运行100万次(100 * 100 * 100),因此将其打印出来需要一段时间。
因为我是奖励积分的傻瓜:这里是使用reduce:
user> (def min-dist {:1 {:1 0 :2 4} :2 {:1 4 :2 0 :3 5} :3 {:2 5 :3 0}})
#'user/min-dist
user> (time
(->> (for [k vertexes i vertexes j vertexes] ;; start by making a sequence of all the combinatioins of indexes
[i j k])
(reduce
(fn [result [i j k]] ;; the reducer function takes the result so far and either modifies it
(let [s (+ (get-in result [i k] inf) ;; or passes it through unchanged.
(get-in result [k j] inf))]
(if (> (get-in result [i j] inf) s) ;; depending on this if expression here.
(assoc-in result [i j] s) ;; pass on the changed value
result))) ;; pass on the original value, unchanged
min-dist))) ;; this argument is the initial value.
;; the ->> expression places the result of the for expression here. (as the last argument)
"Elapsed time: 5.099 msecs"
{:1 {:1 0, :2 4, :3 9}, :2 {:1 4, :2 0, :3 5}, :3 {:2 5, :3 0, :1 9}}
这使得min-dist中的原始值保持不变。