Clojure:删除树的所有叶子中的值

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

我在clojure中实现了一个trie,但我正在努力使用remove values函数。我使用的结构如下所示:

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