如何使用clojure.zip实现walk/postwark遍历

如何使用clojure.zip实现walk/postwark遍历,clojure,Clojure,我不知道如何使用clojure.zip实现clojure.walk/postwark函数: (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]]] 编辑:对于预走,只需在下楼前执行z/replace (d

我不知道如何使用clojure.zip实现clojure.walk/postwark函数:

(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]]]
编辑:对于预走,只需在下楼前执行
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)))

我认为有一种更惯用的方法来实现保留拉链式导航的后序遍历:

(defn邮政编码)
[loc]
从最左边的孩子开始
(回路[loc loc]
(如果(z/分支机构?loc)
(重复(z/向下位置))
loc)))
(下一站)
[loc]
(如果让[sib(z/右loc)]
如果我们有一个右兄弟姐妹,那就移到它最左边的孩子那里去
(邮政编码sib)
如果没有合适的兄弟姐妹,尽可能向上移动
(如果出租[母公司(z/up loc)]
父母亲
否则我们就完了
[(z/节点位置):结束])
这允许您以与普通拉链相同的方式进行条件修改(即,您不必总是
z/replace
,有时您可以在不更改当前节点的情况下调用
post next

给定这些辅助函数,postwalk的实现变得非常简单:

(defn postwalk[f拉链]
(环[loc(邮政拉链)]
(如果(z/end?loc)
(z/节点位置)
(重复(下一次发布后(z/替换loc(f)(z/节点loc‘‘‘‘‘‘‘‘‘‘‘‘‘‘)’)))

此解决方案的优点还在于不会引入递归,因为递归可能会使大型树的堆栈溢出。

我花了整个下午的时间在这方面,现在,该代码在我看来很神奇。你确定要使用拉链吗?@zcaudate prewalk Added我相信使用它们有一个有趣的用例,因为它比clojure.walk版本更通用。我正在编写自己的zipper实现,但有以下区别:1。它基于元素之间的空格进行操作:[|对于无效状态……很难定义标准行为。有时我们希望它抛出异常,有时我们希望它返回nil,有时我们希望它返回到以前正常的状态。因此,重新启动是让程序员自己定义它的好方法。
(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)))