英文:
Atom and closure in Clojure
问题
我尝试学习 https://clojure.org/reference/atoms 中的 memoize 示例时,我感到困惑,不明白为什么 mem 变量在多次函数调用间保持相同的原子对象。
根据 https://stackoverflow.com/questions/14874970/clojure-closure 中的问答,这是闭包的一种行为。然而,由于下面两个函数的行为不同,我开始认为这是原子的一种行为。你能解释一下以下两个函数的差异,从闭包和原子的视角来看吗?
(def counter
(let [count (atom 0)]
(fn [] (do (swap! count inc)
@count))))
(println (counter)) ;; 1
(println (counter)) ;; 2
(def counter2
(let [count {:value 0}]
(fn () (do (assoc count :value (inc (get count :value)))
(get count :value)))))
(println (counter2)) ;; 0
(println (counter2)) ;; 0
(编辑)
阅读 Eugene 的回答后,我用以下函数替换了第二个示例函数。计数器增加其值的原因是计数器是一个闭包,闭包对可变值进行了封闭。
(def counter2
(let [count (int-array '(0))]
(fn [] (do (aset count 0 (inc (get count 0)))
(get count 0))))) ;; 此行非必需,但为了与上面的函数保持一致而添加。
(println (counter2)) ;; 1
(println (counter2)) ;; 2
英文:
When I tried to learn memoize example in https://clojure.org/reference/atoms , I became confused why mem variable holds the same atom object across multiple function calls.
According to the QA in https://stackoverflow.com/questions/14874970/clojure-closure , this is a behavior of closure. However as the following two functions behave differntly, I come to think that this is a behavior of atom. Could you explain the difference of the following two functions from the view of closure and atom?
(def counter
(let [count (atom 0)]
(fn [] (do (swap! count inc)
@count))))
(println (counter)) ;; 1
(println (counter)) ;; 2
(def counter2
(let [count {:value 0}]
(fn [] (do (assoc count :value (inc (get count :value)))
(get count :value)))))
(println (counter2)) ;; 0
(println (counter2)) ;; 0
(EDIT)
After reading Eugene's answer I replace the second example function with the following function. The reason why the counter increases its value is that the counter is a closure being closed over a variable with mutable value.
(def counter2
(let [count (int-array '(0))]
(fn [] (do (aset count 0 (inc (get count 0)))
(get count 0))))) ;; This line is not necessary but is added to keep consistency with the functions above.
(println (counter2)) ;; 1
(println (counter2)) ;; 2
答案1
得分: 4
Both functions are closed over a single value. The first function is closed over a container whose value can be changed. The second function is closed over an immutable map that cannot be changed.
swap!
changes the value inside an atom, in-place. (BTW don't mix atom operations like you did with swap!
followed by @
unless you're absolutely certain it must be done since it makes the code not atomic. In this case, just return the result of swap!
.)
assoc
creates a new immutable map and returns it, without affecting its arguments.
英文:
Both functions are closed over a single value. The first function is closed over a container whose value can be changed. The second function is closed over an immutable map that cannot be changed.
swap!
changes the value inside an atom, in-place. (BTW don't mix atom operations like you did with swap!
followed by @
unless you're absolutely certain it must be done since it makes the code not atomic. In this case, just return the result of swap!
.)
assoc
create a new immutable map and returns not, without affecting its arguments.
答案2
得分: 1
The new example after editing uses a Java array, which is mutable. Aset can be used to mutate a Java array, aset documentation. Thus you are changing the value in an actual memory location, instead of getting a new object as you would with an immutable data structure.
You lose atomicity and thread safety when you start using a Java int array like this. So the second example is a bit of an anti-pattern.
Clojure data structures are immutable, but Clojure has a lot of interop functionality to work with Java, as it runs on the JVM.
英文:
The new example after editing uses a Java array, which is mutable. Aset can be used to mutate a Java array, aset documentation. Thus you are changing the value in an actual memory location, instead of getting a new object as you would with an immutable data structure.
You lose atomicity and thread safety when you start using a Java int array like this. So the second example is a bit of an anti-pattern.
Clojure data structures are immutable, but Clojure has a lot of interop functionality to work with Java, as it runs on the JVM.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论