2012-12-04 56 views
1

的動機是檢查深嵌套圖中的變化,類似於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的地圖是持久性數據結構,有可能是一種智能算法通過查看底層結構圖了這一點,而不是瀏覽嵌套地圖並比較差異。

回答

1

持久數據結構只是關於實現,而不是「底層結構「。 正如Joost所說(+1),你可以使用「diff」。它只需要用你的答案轉換 「{:鍵...:值...}」 的格局:

(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,你的情況,你可以通過修改的HashMap 「assoc-in」而不是「update-in」:

(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}} 
2

我會想象一個簡單的遞歸算法,如(if (= map1 map2) :no-change (recursively-check-children ...))已經很好地利用了結構共享,除非你處理非常寬的樹(每個節點有很多分支)。在任何情況下,您可能都希望查看clojure.data/diff,它解決了您的問題的一般性非遞歸版本,並且也是通過執行直接操作開始的(= a b)