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 マップは永続的なデータ構造であるため、ネストされたマップを調べて違いを比較するのではなく、基礎となる構造を調べることによってこれを理解するスマートなアルゴリズムが存在することを願っています。

4

2 に答える 2

2

(if (= map1 map2) :no-change (recursively-check-children ...))非常に幅の広いツリー (ノードごとに多数のブランチ) を扱っていない限り、すでに構造共有を十分に活用しているような単純な再帰アルゴリズムを想像します。いずれにせよ、あなたの問題の一般的な非再帰的なバージョンを解決するclojure.data/diffをチェックアウトすることをお勧めします。(= a b)

于 2012-12-04T07:56:23.480 に答える
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})

ところで、「update-in」の代わりに「assoc-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}}
于 2012-12-04T11:02:46.417 に答える