Clojure在州内的州内陈述

时间:2011-11-14 04:40:20

标签: clojure state hierarchy

我很想听听Clojure大师们在层次结构中管理国家的建议。我发现我经常使用{:structures {:like {:this {:with {:many 'levels}} } } }并且如果我想跟踪多个级别的状态变化,通过在(atom {:like (atom 'this)} )值周围抛出原子,我发现自己认为这一定是错的。通常最好只在顶层使用一个原子,并且在地图中没有任何值作为值吗?

3 个答案:

答案 0 :(得分:13)

如果可能的话,不要在数据结构中使用嵌套原子。

主要原因是不变性是你的朋友。 Clojure是一种在不可变数据结构上蓬勃发展的函数式语言。大多数库都采用不可变的数据结构。 Clojure的STM采用不可变数据结构来获得最佳的并发性。不变性使您有机会在任何一个瞬间拍摄整个州的一致快照。对不可变数据进行操作的纯函数易于开发和测试。

如果将原子放入数据结构中,那么就会失去不可变性的所有优点,并且会使代码变得非常复杂 - 如果数据结构中包含大量可变组件,则要更加难以推理数据结构。

一些建议的替代方法:

  • 将整个数据结构放在一个ref或atom中。这可能是一个巨大的数据结构,没有任何问题 - 我曾经写过一个游戏,整个游戏地图都在一个原子上没有任何困难。
  • 使用旨在访问和更改嵌套不可变数据结构的各种方法:assoc-inget-inupdate-in等。
  • 使用递归函数使您的数据结构导航更易于管理。如果你的结构的一个节点有相同“类型”的子节点,那么通常你应该使用某种形式的递归函数。

答案 1 :(得分:12)

您可以使用assoc-inget-inupdate-indissoc-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)

我更倾向于在顶层使用一个原子,因为这会使事情变得非常简单,并且还表明数据代表一个状态,该状态由操作立即修改。如果你把原子放在每个层次上,那么它就会变得太复杂而无法弄清楚发生了什么。另外,如果你的情况下嵌套方式太深,我建议你坐下来仔细考虑是否需要这样的结构,或者可以有更好的替代方案,因为这肯定会导致复杂性,直到嵌套数据递归为止(即每个级别的结构相同)