I am going through this article on Tree Visitors in Clojure and came across the below example:
(def data [[1 :foo] [2 [3 [4 "abc"]] 5]])
(walk/postwalk #(do (println "visiting:" %) %) data)
What is the outer form of postwalk doing? I can't understand its utility. How and why is postwalk used? Any explanations will be appreciated.
I'm not sure if you're asking what #() means or what the purpose of do(form1 form2) means, so I'll answer both.
#()
is a shorthand for declaring an anonymous function. Anonymous functions are useful when you're passing some function into another function.
To illustrate, look at this in the 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
refers to the arguments to positional arguments to a function where n
means nth
argument, starting at 1 As a further shorthand you can use % to refer to the first argument which works well for single arg anonymous functions. This is what you have in your example.
So you example is equivalent to
(def data [[1 :foo] [2 [3 [4 "abc"]] 5]])
(defn f [x] (do (println "visiting:" x) x))
(walk/postwalk f data)
The do
here is a special form, which, from the docs:
(do exprs*) Evaluates the expressions in order and returns the value of the last. If no expressions are supplied, returns nil.
In fact defn
already has an implicit do, so my example above doesn't actually need the 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)
I had the same question today and find this necrotopik. Maybe later is better the newer. Find this on clojure api reference:
postwalk function Usage: (postwalk f form) Performs a depth-first, post-order traversal of form. Calls f on each sub-form, uses f's return value in place of the original. Recognizes all Clojure data structures except sorted-map-by. Consumes seqs as with doall.
Also this example from clojuredocs makes things clearer:
(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]]}]
So, postwalk replaces every subform with result of the function. It is used for updating nested structures with new values. That is why resulting function contains x or % at the end. Adding input to result leads to even more nested stucture.
In the example above it is seen as it walks through the depths of nested map stucture. It is going first on deepest elements of the map, then go up on higher level, then lurk down to the next form, then again goes up and finishes its last move on the whole form.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With