Clojure在州内的州内陈述

Hen*_*gon 14 state clojure hierarchy

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

mik*_*era 13

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

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

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

一些建议的替代方法:

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


iva*_*ant 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
Run Code Online (Sandbox Code Playgroud)

您可以使用它->来使其更具可读性:

(-> @m
    (get-in [:like :this :nested])
    deref
    (get-in [:value]))
; => 5
Run Code Online (Sandbox Code Playgroud)

关于嵌套的原子/参考/代理等,我认为这取决于你想要实现的目标.理解事物当然更容易,如果只有一个在顶部,并且变化是同步的.

另一方面,如果你不需要这种同步,你就会浪费时间去做,而你最好使用嵌套的atoms/refs/agents.

最重要的是,我认为无论哪种方式都是"正确的方式",它们都有其用途.


Ank*_*kur 8

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