给定以下树(或Clojure中的任何其他形式,包括地图和向量):
'( (a b) (c d) )
Run Code Online (Sandbox Code Playgroud)
我想在Clojure中生成一个映射,它根据整个表单的深度优先遍历索引每个子表单,并提供表单子节点索引的向量(或列表)(如果有的话).
0 -> a []
1 -> b []
2 -> (a b) [0 1]
3 -> c []
4 -> d []
5 -> (c d) [3 4]
6 -> ( (a b) (c d) ) [2 5]
Run Code Online (Sandbox Code Playgroud)
到目前为止,我只设法使用clojure.walk生成第一部分(索引子表单),但我对如何生成子项的索引感到困惑.我的代码附加在最后并产生:
user=> (depthFirstIndexing '( (a b) (c d) ))
{6 ((a b) (c d)), 5 (c d), 4 d, 3 c, 2 (a b), 1 b, 0 a}
Run Code Online (Sandbox Code Playgroud)
因此,根据深度优先遍历正确生成子表单的索引,但我不知道如何获取每个子表单的子节点的索引.我试图使用拉链模块,但我看不到如何执行深度优先遍历来收集索引.
(use 'clojure.walk)
(defn depthFirstIndexing [aform]
(let [counter (atom -1)
idxToSubform (atom {})
]
(postwalk (fn [x]
(def idx (swap! counter inc))
(swap! idxToSubform assoc idx x)
x)
aform)
@idxToSubform))
Run Code Online (Sandbox Code Playgroud)
A walk是递归的,不提供累加器参数,这就是你不得不求助于更新原子的原因.
A zipper是迭代的,因此您可以携带其他信息而不会破坏功能模式.
自然深度优先遍历是一个预订遍历,但您是在下订单后,所以这需要一些额外的工作.
这是使用拉链的后订单遍历:
(require '[clojure.zip :as z])
(defn dfs-post-order-traversal [zipper]
(loop [loc zipper, a []]
(cond
(z/end? loc)
(conj a (z/node loc))
(z/branch? loc)
(recur (z/next loc) a)
:else
(recur
(z/next loc)
(into
(conj a (z/node loc))
(reverse
(drop
((fnil count [nil]) (z/path (z/next loc)))
(z/path loc))))))))
Run Code Online (Sandbox Code Playgroud)
和测试用例:
(dfs-post-order-traversal (z/seq-zip '((a b) (c d))))
=> [a b (a b) c d (c d) ((a b) (c d))]
Run Code Online (Sandbox Code Playgroud)
现在要完成您的请求,我们需要将树位置映射回其索引:
(defn dfs-post-order-indexing [branch? children root]
(let [pot (dfs-post-order-traversal (z/zipper branch? children conj root))
m (zipmap pot (range))]
(for [n pot] [(m n) n (if (branch? n) (map m (children n)) (list))])))
(dfs-post-order-indexing seq? identity '((a b) (c d)))
=> ([0 a ()]
[1 b ()]
[2 (a b) (0 1)]
[3 c ()]
[4 d ()]
[5 (c d) (3 4)]
[6 ((a b) (c d)) (2 5)])
Run Code Online (Sandbox Code Playgroud)
更奇特的东西:
(dfs-post-order-indexing coll? seq [{:a :b :c :d} :e [:f [:g '(:h :i)]]])
=> ([0 :a ()]
[1 :b ()]
[2 [:a :b] (0 1)]
[3 :c ()]
[4 :d ()]
[5 [:c :d] (3 4)]
[6 {:a :b, :c :d} (2 5)]
[7 :e ()]
[8 :f ()]
[9 :g ()]
[10 :h ()]
[11 :i ()]
[12 (:h :i) (10 11)]
[13 [:g (:h :i)] (9 12)]
[14 [:f [:g (:h :i)]] (8 13)]
[15 [{:a :b, :c :d} :e [:f [:g (:h :i)]]] (6 7 14)])
Run Code Online (Sandbox Code Playgroud)
(use '[clojure.walk :only (postwalk)])
(use '[clojure.set :only (map-invert)])
(defn idx [col]
(let [m (map vector
(range)
(let [v (atom [])]
(postwalk (fn [f] (swap! v conj f) f) col)
@v))
rm (map-invert m)]
(into {} (map (fn [[i e]]
[i [e (if (sequential? e)
(mapv rm e)
[])]])
m))))
(idx '((a b) (c d)))
=> {0 [a []],
1 [b []],
2 [(a b) [0 1]],
3 [c []],
4 [d []],
5 [(c d) [3 4]],
6 [((a b) (c d)) [2 5]]}
Run Code Online (Sandbox Code Playgroud)