假设我有一个副作用自由函数,我使用相同的参数重复使用,而不将结果存储在变量中。 Clojure是否注意到这一点并使用函数的预先计算值或者是否一直重新计算值?
示例:
(defn rank-selection [population fitness]
(map
#(select-with-probability (sort-by fitness population) %)
(repeatedly (count population) #(rand))))
(defn rank-selection [population fitness]
(let [sorted-population (sort-by fitness population)]
(map
#(select-with-probability sorted-population %)
(repeatedly (count population) #(rand)))))
在第一个版本sort-by
执行n
- 次(其中n
是人口的大小)。
在第二个版本sort-by
执行一次,结果使用n
- 次
Clojure会存储结果吗? 这些方法是否可以快速比较?
答案 0 :(得分:1)
在Clojure中,序列是懒惰的,但语言的其余部分,包括功能评估,都是渴望的。 Clojure每次都会为您调用该函数。使用排名选择功能的第二个版本。
答案 1 :(得分:1)
除非您在代码中指定,否则Clojure不会存储结果,方法是使用注释中提到的memoize
或将计算/结果保存在本地绑定中,就像您一样。
关于一个函数关于另一个函数有多快的问题,这里有一些代码返回执行每个函数的时间(我不得不模拟select-with-probability
函数)。强制评估doall
的结果需要map
。
(defn select-with-probability [x p]
(when (< p 0.5)
x))
(defn rank-selection [population fitness]
(map
#(select-with-probability (sort-by fitness population) %)
(repeatedly (count population) rand)))
(defn rank-selection-let [population fitness]
(let [sorted-population (sort-by fitness population)]
(map
#(select-with-probability sorted-population %)
(repeatedly (count population) rand))))
(let [population (take 1000 (repeatedly #(rand-int 10)))]
(time (doall (rank-selection population <)))
(time (doall (rank-selection-let population <)))
;; So that we don't get the result seq
nil)
这会在我的本地环境中返回以下内容:
"Elapsed time: 95.700138 msecs"
"Elapsed time: 1.477563 msecs"
nil
为了避免使用let
表单,您还可以使用partial
接收函数和任意数量的参数,并返回该函数的部分应用程序以及参数的值提供。结果代码的性能与let
形式的性能顺序相同,但更简洁,可读。
(defn rank-selection-partial [population fitness]
(map
(partial select-with-probability (sort-by fitness population))
(repeatedly (count population) rand)))
(let [population (take 1000 (repeatedly #(rand-int 10)))]
(time (doall (rank-selection-partial population <)))
;; So that we don't get the result seq
nil)
;= "Elapsed time: 0.964413 msecs"