在Clojure函数中重新绑定var的具体缺点是什么?

noa*_*hlz 3 clojure

在探索Clojure中计算阶乘的各种方法时,我提出了以下(非惯用)函数:

(defn factorial-using-do-dotimes [x]
  (do
    (def a 1)
    (dotimes [i x]
      (def a (* a (inc i)))))
  a)
Run Code Online (Sandbox Code Playgroud)

REPL:

user=> (factorial-using-do-dotimes 5)
120
Run Code Online (Sandbox Code Playgroud)

这有什么具体的缺点(除了"非惯用")?性能?正确性(即可能的缺陷)?

Art*_*ldt 10

如果您尝试并行运行此代码,那么竞争条件可能会
导致它无声地产生错误的答案

首先运行它自己:

core> (factorial-using-do-dotimes 10)
3628800                                                                                                    
Run Code Online (Sandbox Code Playgroud)

然后运行两个副本,但让快速的一个提前完成:

core> (do (future (do (java.lang.Thread/sleep 5) 
                      (factorial-using-do-dotimes  1200))) 
          (factorial-using-do-dotimes 10))
3628800                                                                                                    
Run Code Online (Sandbox Code Playgroud)

然后将它们靠得更近:

core> (do (future (do (java.lang.Thread/sleep 1) 
                      (factorial-using-do-dotimes 1200))) 
          (factorial-using-do-dotimes 10))
3628800    
Run Code Online (Sandbox Code Playgroud)

然后同时运行它们:

core> (do (future (do (java.lang.Thread/sleep 0) 
                      (factorial-using-do-dotimes 1200))) 
          (factorial-using-do-dotimes 10))
54698277723986154311681531904000000N                                                                       
Run Code Online (Sandbox Code Playgroud)

这不是正在运行的任何一个因子的答案.
(factorial-using-do-dotimes 1200)长3176位

而答案出错了.

注意:我改变了使用factorial-using-do-dotimes函数*'而不是*这样,我可以在一个更大的例子上运行它,以便在示例中更容易命中.