动机是检查深度嵌套地图中的变化,有点像update-in
的反面。
这是一个简单的例子:
(def p1 {:a {:a1 :1 :a2 :2}
:b {:b1 :1 :b2 :2}})
(def p2 (update-in p1 [:a :a1] (constantly :updated))
;; => {:a {:a1 :updated :a2 :2}
;; :b {:b1 :1 :b2 :2}}
(what-changed? p1 p2)
;; => {:keys [:a :a1] :value :updated)
(what-changed? p2 p1)
;; => {:keys [:a :a1] :value :2)
我希望因为clojure映射是持久性数据结构,所以可能有一种智能算法可以通过查看底层结构来解决这个问题,而不是遍历嵌套映射并比较差异。
答案 0 :(得分:2)
我认为像(if (= map1 map2) :no-change (recursively-check-children ...))
这样的直接递归算法已经很好地利用了结构共享,除非你处理的是非常宽的树(每个节点有很多分支)。在任何情况下,您可能希望查看clojure.data/diff,它解决了问题的一般性,非递归版本,并且也可以通过直接(= a b)
答案 1 :(得分:1)
持久性数据结构仅与实现有关,而与“查看底层结构”无关。 正如Joost所说(+1)你可以使用“diff”。它只需要使用“{:keys ...:value ...}”模式转换答案:
(def p1 {:a {:a1 :1 :a2 :2}
:b {:b1 :1 :b2 {:b11 :11 :b22 :22}}})
(def p2 {:a {:a1 :updated1 :a2 :2}
:b {:b1 :1 :b2 {:b11 :updated2 :b22 :updated3}}})
(defn what-changed?* [m]
(if (not (map? m))
[(list m)]
(apply concat (map (fn [k]
(map (fn [nest-k]
(conj nest-k k))
(nested-keys (m k))))
(keys m)))))
(defn what-changed? [m1 m2]
(map (fn [l] {:keys (drop-last l) :value (last l)})
(nested-keys (second (data/diff m1 m2)))))
测试:
(what-changed? p1 p2)
-> ({:keys (:a :a1), :value :updated1}
{:keys (:b :b2 :b11), :value :updated2}
{:keys (:b :b2 :b22), :value :updated3})
(what-changed? p2 p1)
-> ({:keys (:a :a1), :value :1}
{:keys (:b :b2 :b11), :value :11}
{:keys (:b :b2 :b22), :value :22})
BTW在您的情况下,您可以通过“assoc-in”而不是“update-in”修改hashmap:
(assoc-in {:a {:a1 :1 :a2 :2}
:b {:b1 :1 :b2 :2}}
[:a :a1] :updated)
-> {:a {:a2 :2, :a1 :updated}
:b {:b2 :2, :b1 :1}}