【问题标题】:Clojure: Idiomatic use of atom and ref?Clojure:原子和参考的惯用用法?
【发布时间】:2013-04-06 08:59:45
【问题描述】:

我正在编写一些 Clojure 代码,该代码将引用映射并增加映射中的键值对。我认为我正确使用了 ref,但我不确定 atom。我需要使用swap吗!更地道?我是 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))

打印

$ lein run
#<Ref@494eaec9: {yellow #<Atom@191410e5: 1>}>
#<Ref@494eaec9: {yellow #<Atom@7461373f: 2>}>

【问题讨论】:

  • 酷,谢谢,贴在那儿

标签: clojure idioms ref stm


【解决方案1】:

这样做不太符合 Clojure 的习惯:在持久数据结构中嵌入可变对象通常会破坏不可变数据结构的全部意义。

我会完全避免使用内部原子,只需要一个与键关联的数字。那么my-map中包含的整个数据结构将是不可变的。

至于线程安全:这实际上取决于您将如何使用它。在这种情况下,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}

编辑:更好的做法是将可变性排除在您的函数之外,并将 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}

【讨论】:

  • 感谢您的回复。我最近一直在思考 clojure,我试图概念化我将如何做一些在命令式世界中似乎需要状态的事情。例如,朴素贝叶斯分类器。在某个地方,必须计算计数,然后我们必须根据这些计数计算权重。您对如何思考/建模在 clojure 上下文中看似固有“有状态”的内容有什么建议吗?有没有比传递变异参考更好的方法来管理“状态”?我觉得我即将理解更大的事情,但有点卡住了......
  • 我很少传递参考/原子。我在 Clojure 中的大部分函数都是纯函数:它们采用纯值,并返回新的更改值。我什至写了一整个游戏,其中世界状态是一个单一的不可变数据结构:github.com/mikera/alchemy。 “诀窍”是将函数视为返回旧数据的更新版本并进行更改的东西,
  • 等等,(lib/setup game) 不会改变可变 PersistentTreeGrid 的游戏吗? world.clj:31
  • @mikera 顺便说一句,(fn [n] (if n (inc n) 1)) 只是(fnil inc 0)
  • @David:不,它返回一个新的游戏,初始化了 :lib 数据结构。请注意,该代码位于as-&gt; 宏块中,因此game 将重新绑定到每个操作的结果。这个看起来有点像变异状态,但实际上是一个纯函数。
猜你喜欢
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
相关资源
最近更新 更多