在探索 Clojure 中计算阶乘的各种方法时,我想出了以下(非惯用的)函数:
(defn factorial-using-do-dotimes [x]
(do
(def a 1)
(dotimes [i x]
(def a (* a (inc i)))))
a)
回复:
user=> (factorial-using-do-dotimes 5)
120
这有什么具体的缺点(除了“非惯用的”)?表现?正确性(即可能的缺陷)?
在探索 Clojure 中计算阶乘的各种方法时,我想出了以下(非惯用的)函数:
(defn factorial-using-do-dotimes [x]
(do
(def a 1)
(dotimes [i x]
(def a (* a (inc i)))))
a)
回复:
user=> (factorial-using-do-dotimes 5)
120
这有什么具体的缺点(除了“非惯用的”)?表现?正确性(即可能的缺陷)?
如果您尝试并行运行此代码,那么竞争条件可能会
导致它默默地产生错误的答案
首先自己运行它:
core> (factorial-using-do-dotimes 10)
3628800
然后运行两份,但让快速的一份提前完成:
core> (do (future (do (java.lang.Thread/sleep 5)
(factorial-using-do-dotimes 1200)))
(factorial-using-do-dotimes 10))
3628800
然后将它们靠得更近:
core> (do (future (do (java.lang.Thread/sleep 1)
(factorial-using-do-dotimes 1200)))
(factorial-using-do-dotimes 10))
3628800
然后同时运行它们:
core> (do (future (do (java.lang.Thread/sleep 0)
(factorial-using-do-dotimes 1200)))
(factorial-using-do-dotimes 10))
54698277723986154311681531904000000N
这不是正在运行的任何一个阶乘的答案。
(factorial-using-do-dotimes 1200) 长度为 3176 位
答案是错误的。
注意:我将 factorial-using-do-dotimes 函数更改为 use*'
而不是,*
因此我可以在更大的示例上运行它,以使示例中的时间更容易命中。
在函数体内使用 def 不是惯用的,排序但不是明确未定义的行为,并且考虑到 vars 的实现方式, using(dotimes .. (def ..))
很可能比 using 慢(loop ... (recur ...))
,尤其是当您使用基本类型时,例如数字,和类型提示。
不对变量进行这种“动态”修改的主要原因是它无缘无故地使您的代码复杂化。在正常情况下,循环/递归和瞬态的一些更惯用的组合应该可以让您获得与 clojure 一样好的性能。它仍然是线程安全的、可预测的和可读的。
编辑:作为一个具体的缺陷:当从多个线程同时调用时,您的示例代码无法正常工作。
在需要同步的环境中,重新绑定 def 可能会产生不可预知的结果。如果需要修改状态,请考虑使用 refs、atoms 或 agent。
您可以像这样在 for 循环中累积值
(defn chk-key-present-in-sos
""
[mapped-sos cmp-key cmp-seq-vals]
(if (seq-of-maps? mapped-sos)
(for [mapped-row mapped-sos
:let [matched-rows mapped-row]
:when (chk-one-val-present mapped-row cmp-key cmp-seq-vals)]
matched-rows)
nil))
您可以使用 reduce 来产生最终结果。
(defn key-pres?
"This function accepts a value (cmp-val) and a vector of vectors
(parsed output from clojure-csv) and returns the match value
back if found and nil if not found.
Using reduce, the function searches every vector row to see
if cmp-val is at the col-idx location in the vector."
[cmp-val cmp-idx csv-data]
(reduce
(fn [ret-rc csv-row]
(if (= cmp-val (nth csv-row col-idx nil))
(conj ret-rc cmp-val)))
[]
csv-data))
并且可以into
用来积累。
最后,您始终可以使用递归并让终止步骤返回累积的斐波那契值。