Clojure:atom和ref的惯用用法?
我正在编写一段Clojure代码,它将引用映射并在映射中增加一个键值对。我想我使用ref是正确的,但我不确定atom。我需要使用交换吗!更地道一点?我是STM和Clojure的新手,这看起来是线程安全/正常的吗?我错过了什么Clojure:atom和ref的惯用用法?,clojure,idioms,ref,stm,Clojure,Idioms,Ref,Stm,我正在编写一段Clojure代码,它将引用映射并在映射中增加一个键值对。我想我使用ref是正确的,但我不确定atom。我需要使用交换吗!更地道一点?我是STM和Clojure的新手,这看起来是线程安全/正常的吗?我错过了什么 (defn increment-key [ref key] (dosync (if (= (get @ref key) nil) (alter ref assoc key (atom 1)) (alte
(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))
印刷品
$ lein run
#<Ref@494eaec9: {yellow #<Atom@191410e5: 1>}>
#<Ref@494eaec9: {yellow #<Atom@7461373f: 2>}>
$lein run
#
#
这样做不是很惯用的Clojure方法:在持久数据结构中嵌入可变对象通常会破坏不可变数据结构的整体特性
我会完全避开内部原子,只需要一个与键相关联的数字。然后mymap
中包含的整个数据结构将是不可变的
至于线程安全性:这实际上取决于您将如何使用它。在这种情况下,ref
似乎有些过分,因为只有当您需要跨多个ref协调事务时才真正需要它,这里没有。可能一个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}
编辑:更好的做法是将可变性排除在函数之外,并将增量键定义为纯函数
(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}
很酷,谢谢,贴在那里我很感激你的回复。最近,我一直在思考clojure,我试图概念化我将如何做一些在命令式世界中似乎需要国家的事情。例如,一个朴素贝叶斯分类器。在某些地方,计数必须被计数,然后我们必须根据这些计数计算重量。在clojure环境中,您对如何思考/建模似乎固有的“有状态”有何建议?有没有比传递变异的引用更好的管理“状态”的方法?我觉得我即将理解一些更大的东西,但我有点卡住了…我很少传递参考/原子。Clojure中的大多数函数都是纯函数:它们接受纯值,并返回新的更改值。我甚至编写了一个完整的游戏,其中世界状态是一个单一的不可变数据结构:。“诀窍”是将函数视为返回旧数据的更新版本并进行更改的东西,等等,是不是
(lib/setup game)
mutate game,这是一个可变的PersistentTreeGrid?world.clj:31@mikera顺便说一下,(fn[n](如果n(incn)1))
只是(fnil inc 0)
@David:no,它返回一个初始化了:lib数据结构的新游戏。请注意,该代码位于as->
宏块中,因此game
将被重新绑定到每个操作的结果。这看起来有点像变异状态,但实际上它是一个纯函数。