如何在从每个节点节点收集值时遍历Clojure中的树?

时间:2015-06-14 06:20:10

标签: clojure tree lisp

我想创建一个从二叉树中的每个节点收集值的函数。在ClojureDocs中,我找到了几个遍历树/图的函数,例如tree-seq,prewalk和postwalk。

https://clojuredocs.org/clojure.core/tree-seq

https://clojuredocs.org/clojure.walk/prewalk

https://clojuredocs.org/clojure.walk/postwalk

是否可以使用其中任何一个来累积遍历的节点的值?作为Clojure newby,我不知道该怎么做。如果你知道(用Clojure或类似的Lispy语言),请告诉我。理想情况下,Clojure newby可以理解你的答案; - )

我的二叉树用这样的节点表示:(值left-child right-child)。例如:

  

(2(7 nil nil)(88(5 nil nil)nil))

从这个例子中,我想要返回的功能(2 7 88 5)。

注意:遍历方法并不重要。我只是想学习一种收集节点值的技术。

2 个答案:

答案 0 :(得分:7)

好吧,tree-seq将为您提供节点序列(深度优先行走)。然后,您可以对其进行任何其他转换,包括(map some-value-extractor-fn (tree-seq ...以获取每个节点中的值。您只需要为该表示选择树表示和适当的函数,以便tree-seq可以知道什么是内部节点及其子节点。 例如,使用树的定义作为嵌套列表:

嵌套列表树

我们的树可以分支的节点是列表,我们可以使用list?来识别它们。他们的孩子是第一个之后的值,即他们的rest。所以我们可以仅使用标准函数来定义tree-seq:

(->> '( 2 (7 nil nil) (88 (5 nil nil) nil) )
     (tree-seq list? rest))

但是这有点垃圾 - 每个nil都显示为seq的成员,我们感兴趣的每个值都出现在其列表节点中以及作为其自身的成员等等。我们可以使用filterremove来清理它 - 例如,我们可以丢弃所有叶子值并仅采用内部节点:

(->> '( 2 (7 nil nil) (88 (5 nil nil) nil) )
     (tree-seq list? rest)
     (filter list?))
;;=> ((2 (7 nil nil) (88 (5 nil nil) nil)) (7 nil nil) (88 (5 nil nil) nil) (5 nil nil))

,然后map first超过这些:

(->> '( 2 (7 nil nil) (88 (5 nil nil) nil) )
     (tree-seq list? rest)
     (filter list?)
     (map first)) ;;=>(2 7 88 5)

或者,我们可以尝试丢弃树的内部节点和零节点,只使用带有值的树叶:

(->> '( 2 (7 nil nil) (88 (5 nil nil) nil) )
     (tree-seq list? seq)
     (remove (some-fn list? nil?))) ;;=>(2 7 88 5)

请注意,在此策略中,我必须使用seq而不是rest,因为我希望列表中的第一个值也是该节点的子级。 (some-fn list? nil?)是一些高阶函数 - 它构建一个函数来检查输入是否满足谓词list? {{1>的 }} (或两者)。

嵌套地图树

如果你想要一个更通用的树定义,每个节点可以包含多个值加上可变数量的子节点,你可以将树定义为嵌套地图:nil?

在这种情况下,仅将地图视为节点通常最简单。我们可能的分支节点是地图 - 使用{:value 2 :children [{:value 7} {:value 88 :children [{:value 5}]}]}检查它。我们将他们的孩子存储在密钥map?中,这是一个关键字,因此也是一个函数。我们使用该功能来获得孩子。

:children

然后你只需要在节点上(->> {:value 2 :children [{:value 7} {:value 88 :children [{:value 5}]}]} (tree-seq map? :children)) ;;=> ({:value 2, :children [{:value 7} {:value 88, :children [{:value 5}]}]} {:value 7} {:value 88, :children [{:value 5}]} {:value 5}) 来获取你想要的数据:

map

答案 1 :(得分:1)

为了累积树的节点值,我有一个非功能性解决方案:

user> (def a (atom 0))
#'user/a
user> (dorun (clojure.walk/postwalk #(when (number? %) (swap! a (partial + %))) '( 2 (7 nil nil) (88 (5 nil nil) nil))))
nil
user> @a
102