Dav*_*ams 3 idioms clojure ref stm
我正在研究一些Clojure代码,它将引用一个map并在地图中增加一个键值对.我想我正在使用ref,但我不确定原子.我需要使用交换!更加惯用?我是STM和Clojure的新手,这看起来是线程安全/理智吗?我错过了什么?
(defn increment-key [ref key]
(dosync
(if (= (get @ref key) nil)
(alter ref assoc key (atom 1))
(alter ref assoc key (atom (inc @(get @ref key)))))))
(defn -main [& args]
(def my-map (ref {}))
(increment-key my-map "yellow")
(println my-map)
(increment-key my-map "yellow")
(println my-map))
Run Code Online (Sandbox Code Playgroud)
打印
$ lein run
#<Ref@494eaec9: {yellow #<Atom@191410e5: 1>}>
#<Ref@494eaec9: {yellow #<Atom@7461373f: 2>}>
Run Code Online (Sandbox Code Playgroud)
这样做不是非常惯用的Clojure:在持久数据结构中嵌入可变对象通常会破坏不可变数据结构的整个点.
我会完全避免内部原子,并且只有一个与键相关联的数字.然后,其中包含的整个数据结构my-map将是不可变的.
至于线程安全:它实际上取决于你将如何使用它.ref在这种情况下,A 似乎有点矫枉过正,因为当您需要协调跨多个参考的交易时,它才真正需要,这是您没有的.atom对于你想要做的事情来说,可能就足够了.
以下是您可以解决的问题:
(defn increment-key [ref key]
(swap! ref update-in [key] (fn [n] (if n (inc n) 1))))
(def my-map (atom {}))
(increment-key my-map "yellow")
(println my-map) ;; => {"yellow" 1}
(increment-key my-map "yellow")
(println my-map) ;; => {"yellow" 2}
Run Code Online (Sandbox Code Playgroud)
编辑:更好的方法是保持函数的可变性,并将increment-key定义为纯函数.
(defn increment-key [m key]
(assoc m key (if-let [n (m key)] (inc n) 1)))
(def my-map (atom {}))
(swap! my-map increment-key "yellow")
(println my-map) ;; => {"yellow" 1}
(swap! my-map increment-key "yellow")
(println my-map) ;; => {"yellow" 2}
Run Code Online (Sandbox Code Playgroud)