Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

walk/postwalk usage

Tags:

clojure

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.

like image 847
murtaza52 Avatar asked Dec 16 '22 00:12

murtaza52


2 Answers

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)
like image 164
sw1nn Avatar answered Jan 28 '23 21:01

sw1nn


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.

like image 41
Rijk Avatar answered Jan 28 '23 20:01

Rijk