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. Consumes seqs as with doall.
(require '[clojure.walk :refer [postwalk]]) (let [counter (atom -1) line-counter (atom 0) print-touch (fn [x] (print (swap! line-counter inc) ":" (pr-str x) "→ ")) change (fn [x] (let [new-x (swap! counter inc)] (prn new-x) [new-x x]))] (postwalk (fn [x] (print-touch x) (change x)) {:a 1 :b 2})) ;; printed output: 1 : :a → 0 2 : 1 → 1 3 : [[0 :a] [1 1]] → 2 4 : :b → 3 5 : 2 → 4 6 : [[3 :b] [4 2]] → 5 7 : {2 [[0 :a] [1 1]], 5 [[3 :b] [4 2]]} → 6 ;; returned value: [6 {2 [[0 :a] [1 1]], 5 [[3 :b] [4 2]]}]
;;example of removing namespaces from all keys in a nested data structure (def thing {:page/tags [{:tag/category "lslsls"}]}) (postwalk #(if (keyword? %) (keyword (name %)) %) thing) {:tags [{:category "lslsls"}]}
(use 'clojure.walk) ;;example of evaluating an expression tree, starting at the leaves (def expression-tree {:function + :children [1 {:function * :children [2 6]}]}) (defn evaluate [node] (if-let [f (:function node)] (apply f (:children node)) node)) (postwalk evaluate expression-tree) => 13
;; an example to show the differences between postwalk and prewalk ;; (see the counterpart at prewalk) (let [counter (atom 0) print-touch (fn [x] (print (swap! counter inc) ":" (pr-str x) "→ ")) change-type (fn [x] (let [new-x (if (vector? x) (apply list x) (str x))] (prn new-x) new-x))] (clojure.walk/postwalk (fn [x] (print-touch x) (change-type x)) [:a [:ba :bb] :c])) ;; printed output: 1 : :a → ":a" 2 : :ba → ":ba" 3 : :bb → ":bb" 4 : [":ba" ":bb"] → (":ba" ":bb") 5 : :c → ":c" 6 : [":a" (":ba" ":bb") ":c"] → (":a" (":ba" ":bb") ":c") ;; returned value: => (":a" (":ba" ":bb") ":c")
;; Recursively sort nested map (require '[clojure.walk :as w]) (def m {:b 2, :c 3, :a 1, :d {:x 10, :c 3, :a 11, :b 22}}) (w/postwalk (fn [x] (if (map? x) (into (sorted-map) x) x)) m) ;;=> {:a 1, :b 2, :c 3, :d {:a 11, :b 22, :c 3, :x 10}}
As of 1.9.0, postwalk passes the k/v pairs of a map to f
not, as one might expect, as type clojure.lang.MapEntry
but as clojure.lang.PersistentVector
(JIRA). As a result, f
cannot distinguish k/v pairs from other two-element vectors.
Alex Miller's article "Tree visitors in Clojure" might be helpful in understanding general tree traversal and the usage of clojure.walk/postwalk
.
As of 1.9.0, postwalk passes the k/v pairs of a map to f not, as one might expect, as type clojure.lang.MapEntry but as clojure.lang.PersistentVector (JIRA). As a result, f cannot distinguish k/v pairs from other two-element vectors.
This was fixed in 1.10.0, and one can use (map-entry? v)
to distinguish a k/v pair from a vector when walk
ing.
Updated link to "Tree visitors in Clojure", mentioned above (PDF):