我建议使用非常规则的 trie 结构:第一个条目总是:value带有 a#{}值的映射。所有其他键都是孩子,它们包含自己尝试的值。(意味着他们一: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)会导致 just
{: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"}}}}