我建议使用非常规则的 trie 结构:
一个映射,其第一个条目始终为 :value,其值为 #{}。
所有其他键都是子项,它们包含自己尝试的值。
(意味着他们在开头有一个:value 键!)
然后为哈希映射定义car/first、cdr/rest、cons、map (-map)。
(defn first-map [m]
(let [k (first (keys m))]
{k (k m)}))
(defn rest-map [m]
(into {} (map (fn [k] {k (k m)}) (rest (keys m)))))
(defn cons-map [m1 m2]
(into {} (concat m1 m2)))
(defn map-map [f m & args]
(into {} (map (fn [k] {k (apply f (k m) args)})
(keys m))))
试试看:
(def m {:a 1 :b 2 :c 3})
(first-map m) ;; => {:a 1}
(rest-map m) ;; => {:b 2 :c 3}
(cons-map {:a 1} {:b 2 :c 3}) ;; => {:a 1 :b 2 :c 3}
(map-map #(+ % 1) m) ;; => {:a 2, :b 3, :c 4}
(map-map #(+ %1 %2) m 1) ;; => {:a 2, :b 3, :c 4}
然后,使用它们定义remove-value
(defn remove-value [trie val]
(cons-map {:value (disj (:value (first-map trie)) val)}
(map-map remove-value (rest-map trie) val)))
原来,我写道:
(defn remove-value [trie val]
(cond (empty? (rest-map trie))
{:value (disj (:value (first-map trie)) val)}
:else (cons-map {:value (disj (:value (first-map trie)) val)}
(map-map remove-value (rest-map trie) val))))
但后来意识到 :else 分支会自动执行此操作,因为 map-map 在空的 (rest-map trie) 上只会导致
{:value (disj (:value (first-map trie)) val)} - 也可以表示为
(let [[k v] (first-map trie)] {k (disj v val)}).
您的trie 将是:
(def trie {:value #{}
:a {:value #{"val1" "val2"}}
:b {:value #{"val2"}
:c {:value #{"val1"}}}})
注意:顶层的树总是以
:value #{}(树的根)。
:value 的值将始终是一个集合,要么是空的 #{},要么包含一个或多个元素。
所以,每个孩子本身就是一个完整的trie。
user=> (remove-value trie "val1")
{:value #{}, :a {:value #{"val2"}}, :b {:value #{"val2"}, :c {:value #{}}}}
user=> (remove-value trie "val2")
{:value #{}, :a {:value #{"val1"}}, :b {:value #{}, :c {:value #{"val1"}}}}