使用clojure.zip对Postorder树进行遍历以编辑节点

时间:2010-07-30 17:12:15

标签: clojure tree traversal zipper

我有一个表示为嵌套向量的树。我想对树进行indexed的推广,显示每个节点的索引,如下所示,

(visit 42); => [0 42]
(visit [6 7]); => [0
              ;     [[0 6] 
              ;      [1 7]]]

天真的实现将直接使用clojure.zip(as already asked here

(defn visit [tree]
  (loop [loc (vector-zip tree)]
    (if (end? loc)
      (root loc)
      (recur 
        (next (edit loc #(conj 
                           [(count (lefts loc))] 
                           %)))))))

但是clojure.zip/next重复执行前序遍历,在这种情况下导致无限循环(未访问的节点无限地将conj编辑到[:found]向量中。另一种方法是使用clojure.walk/postwalk,但它不提供结构信息,例如索引。

你会如何实现这个?是否有postorder-next的拉链可以立即解决它?

1 个答案:

答案 0 :(得分:4)

我不确定我是否理解你要做的事情,但是这些例子告诉我,分配给节点的索引对应于他们的左兄弟的数量(因为在第二个例子中都是根节点)并且6孩子标有0)。 更新:显然我只是第一次误读visit示例 - 它使意图足够清楚......幸运的是,现在我正确阅读了它,在我看来,下面的答案是正确的。< / em>的

如果这是正确的,这是一个基于clojure.walk/postwalk的解决方案:

(defn index-vec-tree [vt]
  [0 (walk/postwalk
       (fn [node]
         (if-not (vector? node)
           node
           (vec (map-indexed vector node))))
       vt)])

通过给出的例子:

user> (index-vec-tree [6 7])
[0 [[0 6] [1 7]]]
user> (index-vec-tree 42)
[0 42]

更新:使用map-indexed的简单解决方案(在1.2中提供;在1.1中使用map + clojure.contrib.seq-utils/indexed):

(defn index-vec-tree [vt]
  (letfn [(iter [i vt] [i (if (vector? vt)
                                (vec (map-indexed iter vt))
                                vt)])]
    (iter 0 vt)))