4

我正在阅读这篇关于 Clojure 中树访问者的文章,并遇到了以下示例:

(def data [[1 :foo] [2 [3 [4 "abc"]] 5]])

(walk/postwalk #(do (println "visiting:" %) %) data)

postwalk的外在形式是做什么的?我无法理解它的用途。如何以及为什么使用 postwalk?任何解释将不胜感激。

4

2 回答 2

7

我不确定您是在问 #() 是什么意思,还是 do(form1 form2) 的目的是什么,所以我都会回答这两个问题。

#()是声明匿名函数的简写。当您将某个函数传递给另一个函数时,匿名函数很有用。

为了说明,请看 repl 中的这个

; define an anonymous function
user=> #(+ %1 %2)
#<user$eval68$fn__69 user$eval68$fn__69@9fe84e>

; is equivalent to 
user => (fn [a b] (+ a b))
#<user$eval1951$fn__1952 user$eval1951$fn__1952@118bd3c>

; furthermore, you could then assign your anonymous function to a var
(def f #(+ %1 %2))

; is equivalent to 
(defn f [a b] (+ a b))

user=> (#(+ %1 %2) 1 2)
3

user=> (f 1 2)
3

The%n指的是函数的位置参数的参数,其中n表示nth参数,从 1 开始作为进一步的简写,您可以使用 % 来引用第一个参数,该参数适用于单 arg 匿名函数。这就是您在示例中所拥有的。

所以你的例子相当于

(def data [[1 :foo] [2 [3 [4 "abc"]] 5]])

(defn f [x] (do (println "visiting:" x) x))

(walk/postwalk f data)

do是一种特殊的形式,来自文档:

(do exprs*) 按顺序计算表达式并返回最后一个的值。如果没有提供表达式,则返回 nil。

事实上defn已经有一个隐含的do,所以我上面的例子实际上并不需要do ...

; your example is equivalent to:

(def data [[1 :foo] [2 [3 [4 "abc"]] 5]])

(defn f [x] (println "visiting:" x) x)

(walk/postwalk f data)
于 2012-05-15T08:52:34.110 回答
5

我今天有同样的问题,找到了这个 necrotopik。也许越晚越好。在clojure api 参考上找到这个:

postwalk 函数用法:(postwalk f 形式)执行深度优先、后序遍历形式。在每个子表单上调用 f,使用 f 的返回值代替原始值。识别除 sorted-map-by 之外的所有 Clojure 数据结构。与 doall 一样使用 seq。

来自 clojuredocs 的这个例子也让事情变得更清楚:

(use 'clojure.walk)
(let [counter (atom -1)]
(postwalk (fn [x]
            [(swap! counter inc) x])
              {:a 1 :b 2}))

  => [6 {2 [[0 :a] [1 1]], 5 [[3 :b] [4 2]]}] 

因此, postwalk 用函数的结果替换每个子表单。它用于使用新值更新嵌套结构。这就是结果函数最后包含 x 或 % 的原因。向结果添加输入会导致更多的嵌套结构。

在上面的示例中,它被视为穿过嵌套地图结构的深处。它首先在地图的最深元素上前进,然后上升到更高的级别,然后潜伏到下一个形式,然后再次上升并完成整个形式的最后一步。

于 2013-06-02T01:59:12.490 回答