我很想听听Clojure大师们在层次结构中管理国家的建议。我发现我经常使用{:structures {:like {:this {:with {:many 'levels}} } } }
并且如果我想跟踪多个级别的状态变化,通过在(atom {:like (atom 'this)} )
值周围抛出原子,我发现自己认为这一定是错的。通常最好只在顶层使用一个原子,并且在地图中没有任何值作为值吗?
答案 0 :(得分:13)
如果可能的话,不要在数据结构中使用嵌套原子。
主要原因是不变性是你的朋友。 Clojure是一种在不可变数据结构上蓬勃发展的函数式语言。大多数库都采用不可变的数据结构。 Clojure的STM采用不可变数据结构来获得最佳的并发性。不变性使您有机会在任何一个瞬间拍摄整个州的一致快照。对不可变数据进行操作的纯函数易于开发和测试。
如果将原子放入数据结构中,那么就会失去不可变性的所有优点,并且会使代码变得非常复杂 - 如果数据结构中包含大量可变组件,则要更加难以推理数据结构。
一些建议的替代方法:
答案 1 :(得分:12)
您可以使用assoc-in
,get-in
,update-in
和dissoc-in
函数来处理嵌套结构。
它们非常方便,但我不知道它们是否可以直接处理原子等。在最坏的情况下,您应该能够将它们嵌套到deref,例如:
(def m (atom {:like {:this {:nested (atom {:value 5})}}}))
@(get-in @m [:like :this :nested])
; => {:value 5}
(get-in @(get-in @m [:like :this :nested]) [:value])
; => 5
您可以使用->
来提高可读性:
(-> @m
(get-in [:like :this :nested])
deref
(get-in [:value]))
; => 5
关于嵌套的atoms / refs / agents等,我认为这取决于你想要实现的目标。如果只有其中一个位于顶部并且更改是同步的,那么推理事物肯定更容易。
另一方面,如果你不需要这种同步,你就会浪费时间去做,而你最好使用嵌套的atoms / refs / agents。
最重要的是,我认为无论哪种方式都是“正确的方式”,他们都有自己的用法。
答案 2 :(得分:8)
我更倾向于在顶层使用一个原子,因为这会使事情变得非常简单,并且还表明数据代表一个状态,该状态由操作立即修改。如果你把原子放在每个层次上,那么它就会变得太复杂而无法弄清楚发生了什么。另外,如果你的情况下嵌套方式太深,我建议你坐下来仔细考虑是否需要这样的结构,或者可以有更好的替代方案,因为这肯定会导致复杂性,直到嵌套数据递归为止(即每个级别的结构相同)