Clojure:删除树的所有叶子中的值
我在clojure中实现了一个trie,但我正在努力使用remove values函数。我使用的结构如下所示:Clojure:删除树的所有叶子中的值,clojure,tree,trie,Clojure,Tree,Trie,我在clojure中实现了一个trie,但我正在努力使用remove values函数。我使用的结构如下所示: (def trie {\a {:value #{"val1" "val2"}} \b {\c {:value #{"val1"}} :value #{"val2"}}}) {\a {:value #{"val2"}} \b
(def trie {\a {:value #{"val1" "val2"}}
\b {\c {:value #{"val1"}}
:value #{"val2"}}})
{\a {:value #{"val2"}}
\b {\c {:value #{}}
:value #{"val2"}}}
我想调用这样一个函数(remove value trie“val1”)
,并获得一个结构,其中“val1”的所有实例都从leave节点中的集合中移除。生成的trie如下所示:
(def trie {\a {:value #{"val1" "val2"}}
\b {\c {:value #{"val1"}}
:value #{"val2"}}})
{\a {:value #{"val2"}}
\b {\c {:value #{}}
:value #{"val2"}}}
或者更好:
{\a {:value #{"val2"}}
\b {:value #{"val2"}}}
根据我在这里看到的,这可能在五行clojure中完成,但我不知道怎么做。此外,我还不喜欢数据结构,如果您需要修改它以使其成为一个惯用版本,请随意,只要它仍然是一个trie。一种方法是使用
postwalk
。e、 g
(def trie {\a {:value #{"val1" "val2"}}
\b {\c {:value #{"val1"}}
:value #{"val2"}}})
; #'user/trie
(require '[clojure.walk :refer [postwalk]])
; nil
(doc postwalk)
; -------------------------
; clojure.walk/postwalk
; ([f form])
; Performs a depth-first, post-order traversal of form. Calls f on
; each sub-form, uses f's return value in place of the original.
; Recognizes all Clojure data structures. Consumes seqs as with doall.
; nil
(postwalk #(if (set? %) (disj % "val1") %) trie)
; {\a {:value #{"val2"}}, \b {\c {:value #{}}, :value #{"val2"}}}
从那里你还可以在地图上做过滤(
dissoc
char键,如果它们不再有任何值)这个比我想象的要复杂!当我试图快速回答问题时,我至少犯了两个错误。以下是来自以下网站的工作版本:
如果您使用
你也可能对这一点感兴趣
对于更复杂的树结构操作,您可能会感兴趣。我建议使用非常规则的trie结构: 第一个条目始终为
:value
且值为{}
的映射。
所有其他键都是包含自己尝试的值的子键。
(这意味着它们在开头有一个:value
键!)
然后为散列映射(-map
)定义car/first
,cdr/rest
,cons
,map
)
试一试:
(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}
然后,使用它们定义删除值
(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
分支会自动执行此操作,因为在空的(rest-map-trie)
上的映射
只会导致
{:value(disj(:value(first-map-trie))val)}
-也可以表示为
(让[[kv](第一个映射trie)]{k(disj v val)})
您的trie
将是:
(def trie {:value #{}
:a {:value #{"val1" "val2"}}
:b {:value #{"val2"}
:c {:value #{"val1"}}}})
注意:toplevel中的树始终以开头
:值#{}
(树的根)。
: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"}}}}
谢谢你的详细解释。我喜欢每根树枝的整齐。
(def trie {:value #{}
:a {:value #{"val1" "val2"}}
:b {:value #{"val2"}
:c {:value #{"val1"}}}})
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"}}}}