我无法弄清楚如何使用clojure.zip实现clojure.walk / postwalk函数:
(clojure.walk/postwalk
#(do (println %)
%)
[1
[2 [3 4 5]]
[6 [7 8]]])
输出:
1
2
3
4
5
[3 4 5]
[2 [3 4 5]]
6
7
8
[7 8]
[6 [7 8]]
[1 [2 [3 4 5]] [6 [7 8]]]
答案 0 :(得分:4)
(defn postwalk [f loc]
(let [loc (if-some [loc (z/down loc)]
(loop [loc loc]
(let [loc (postwalk f loc)]
(if-some [loc (z/right loc)]
(recur loc)
(z/up loc))))
loc)]
(z/replace loc (f (z/node loc)))))
=> (postwalk #(doto % prn) (z/vector-zip [1 [2 [3 4 5]] [6 [7 8]]]))
1
2
3
4
5
[3 4 5]
[2 [3 4 5]]
6
7
8
[7 8]
[6 [7 8]]
[1 [2 [3 4 5]] [6 [7 8]]]
编辑:进行预走路,只需在下降前执行z/replace
。
(defn prewalk [f loc]
(let [loc (z/replace loc (f (z/node loc)))]
(if-some [loc (z/down loc)]
(loop [loc loc]
(let [loc (prewalk f loc)]
(if-some [loc (z/right loc)]
(recur loc)
(z/up loc))))
loc)))
答案 1 :(得分:0)
我相信,有一种更惯用的方式来实现订单后遍历,从而保留拉链式导航:
(defn post-zip
[loc]
;; start from the deepest left child
(loop [loc loc]
(if (z/branch? loc)
(recur (z/down loc))
loc)))
(defn post-next
[loc]
(if-let [sib (z/right loc)]
;; if we have a right sibling, move to it's deepest left child
(post-zip sib)
;; If there is no right sibling move up if possible
(if-let [parent (z/up loc)]
parent
;; otherwise we are done
[(z/node loc) :end])))
这使您可以像使用普通拉链一样进行条件修改(即,您不必总是z/replace
,有时您可以直接调用post-next
而无需更改当前节点)。
考虑到这些辅助功能,postwalk的实现变得非常简单:
(defn postwalk [f zipper]
(loop [loc (post-zip zipper)]
(if (z/end? loc)
(z/node loc)
(recur (post-next (z/replace loc (f (z/node loc))))))))
此解决方案还具有不引入递归的优势,因为递归可能会使大型树的堆栈溢出。