Map 去原子化地图

Map 去原子化地图,map,clojure,Map,Clojure,我在映射中有原子,这可能是个好主意,也可能不是个好主意,但关键是我需要对原子进行去引用,这样我就可以在映射中使用json str,而json str无法处理原子,所以我写了以下内容: (defn deatomize- [m] (cond (instance? clojure.lang.Atom m) #(deatomize- @m) (map? m) (zipmap (keys m) (map #(trampoline deatomize- %) (vals m))) :el

我在映射中有原子,这可能是个好主意,也可能不是个好主意,但关键是我需要对原子进行去引用,这样我就可以在映射中使用json str,而json str无法处理原子,所以我写了以下内容:

(defn deatomize- [m]
 (cond
   (instance? clojure.lang.Atom m) #(deatomize- @m)
   (map? m) (zipmap (keys m) (map #(trampoline deatomize- %) (vals m)))
   :else m
 )
)

(defn deatomize [m] (trampoline deatomize- m))

这似乎是可行的,但是a)它好吗,b)有更好的方法吗?

我认为您的代码会很好地工作

一些一般性反馈:

  • 贴图中的原子有点反模式——通常你会尝试将贴图放在原子中。其思想是尽可能地保持数据结构不变,并使引用以相当细粒度的级别保存整个数据结构。我强烈建议重新考虑这种设计——从长远来看,它可能会伤害您(例如,大多数Clojure库函数将采用不可变映射)
  • 你没有去原子化钥匙。也许你从来没有在键中使用过原子,在这种情况下这很好,但我认为这是值得指出的
  • 通常在Clojure中,右括号没有自己的一行,而是在前一行的末尾。一开始可能会觉得奇怪,但这是一种很好的Lisp风格,您可能应该采用它
  • 此函数的性能可能不太好,因为它正在逐块重建整个地图结构,即使没有任何更改。reduce可以用来改进这一点,只需要在需要时修改地图
  • 我认为测试clojure.lang.IDeref比测试clojure.lang.Atom更好。通过这样做,如果需要,您的代码还将处理其他引用类型
  • trampoline将增加开销,仅在数据结构中存在很深的嵌套(可能会导致堆栈溢出)的情况下才需要。如果您需要安全堆栈处理和良好的性能,那么您可能会考虑递归实现,并且在StasOfFuffExchange异常情况下回落到蹦床版本。
  • 如果找到一个atom,函数实际上是尾部递归的,因此可以使用recur。这将减少使用蹦床的需要
这里有一个可供考虑的替代方案:

(defn deatomize [m] 
  (cond
    (instance? clojure.lang.IDeref m) 
      (recur @m)
    (map? m) 
      (reduce 
        (fn [current-map [k v]] 
          (let [nv (deatomise v)] 
            (if (= v nv) current-map (assoc current-map k nv)))) 
        m 
        m)
    :else m))

谢谢你全面的回答!我会从中学到很多。我对地图上的原子一点都不满意,所以现在我要想办法重新设计。恐怕我不能同意结束括号,但是,我必须把他们放在一个新行,以配合他们同意的功能,否则我会感到困惑。另外,在它们之间添加和删除行也更容易。我不知道你可以像那样使用recur-没有(循环-所以可以将recur与任何绑定形式一起使用(这是术语吗?像let、loop、defn、fn等)?另请参阅我前面的问题@Hendekagon-默认情况下,recur可用于循环或fn-它也可用于其他绑定形式,只要它们是扩展到循环或fn的宏(defn就是一个例子)@Hendekagon-尝试使用“rainbow parens”如果你的编辑器支持它们,这应该比parens匹配的新行更清晰。然而,它们确实会让你的代码看起来像一个小孩用一盒蜡笔攻击它。