; Copyright (c) Rich Hickey. All rights reserved. ; The use and distribution terms for this software are covered by the ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ; which can be found in the file epl-v10.html at the root of this distribution. ; By using this software in any fashion, you are agreeing to be bound by ; the terms of this license. ; You must not remove this notice, or any other, from this software. ;;; walk.cljs - generic tree walker with replacement ;; by Stuart Sierra ;; Jul5 17, 2011 ;; CHANGE LOG: ;; ;; * July 17, 2011: Port to ClojureScript ;; ;; * December 15, 2008: replaced 'walk' with 'prewalk' & 'postwalk' ;; ;; * December 9, 2008: first version (ns ^{:author "Stuart Sierra", :doc "This file defines a generic tree walker for Clojure data structures. It takes any data structure (list, vector, map, set, seq), calls a function on every element, and uses the return value of the function in place of the original. This makes it fairly easy to write recursive search-and-replace functions, as shown in the examples. Note: \"walk\" supports all Clojure data structures EXCEPT maps created with sorted-map-by. There is no (obvious) way to retrieve the sorting function."} clojure.walk) (defn walk "Traverses form, an arbitrary data structure. inner and outer are functions. Applies inner to each element of form, building up a data structure of the same type, then applies outer to the result. Recognizes all Clojure data structures. Consumes seqs as with doall." {:added "1.1"} [inner outer form] (cond (list? form) (outer (apply list (map inner form))) (map-entry? form) (outer (MapEntry. (inner (key form)) (inner (val form)) nil)) (seq? form) (outer (doall (map inner form))) (record? form) (outer (reduce (fn [r x] (conj r (inner x))) form form)) (coll? form) (outer (into (empty form) (map inner form))) :else (outer form))) (defn postwalk "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." {:added "1.1"} [f form] (walk (partial postwalk f) f form)) (defn prewalk "Like postwalk, but does pre-order traversal." {:added "1.1"} [f form] (walk (partial prewalk f) identity (f form))) (defn keywordize-keys "Recursively transforms all map keys from strings to keywords." {:added "1.1"} [m] (let [f (fn [[k v]] (if (string? k) [(keyword k) v] [k v]))] ;; only apply to maps (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m))) (defn stringify-keys "Recursively transforms all map keys from keywords to strings." {:added "1.1"} [m] (let [f (fn [[k v]] (if (keyword? k) [(name k) v] [k v]))] ;; only apply to maps (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m))) (defn prewalk-replace "Recursively transforms form by replacing keys in smap with their values. Like clojure/replace but works on any data structure. Does replacement at the root of the tree first." {:added "1.1"} [smap form] (prewalk (fn [x] (if (contains? smap x) (smap x) x)) form)) (defn postwalk-replace "Recursively transforms form by replacing keys in smap with their values. Like clojure/replace but works on any data structure. Does replacement at the leaves of the tree first." {:added "1.1"} [smap form] (postwalk (fn [x] (if (contains? smap x) (smap x) x)) form))