Mercurial > lasercutter
diff src/clojure/walk.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/walk.clj Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,132 @@ 1.4 +; Copyright (c) Rich Hickey. All rights reserved. 1.5 +; The use and distribution terms for this software are covered by the 1.6 +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 1.7 +; which can be found in the file epl-v10.html at the root of this distribution. 1.8 +; By using this software in any fashion, you are agreeing to be bound by 1.9 +; the terms of this license. 1.10 +; You must not remove this notice, or any other, from this software. 1.11 + 1.12 +;;; walk.clj - generic tree walker with replacement 1.13 + 1.14 +;; by Stuart Sierra 1.15 +;; December 15, 2008 1.16 + 1.17 +;; CHANGE LOG: 1.18 +;; 1.19 +;; * December 15, 2008: replaced 'walk' with 'prewalk' & 'postwalk' 1.20 +;; 1.21 +;; * December 9, 2008: first version 1.22 + 1.23 + 1.24 +(ns 1.25 + ^{:author "Stuart Sierra", 1.26 + :doc "This file defines a generic tree walker for Clojure data 1.27 +structures. It takes any data structure (list, vector, map, set, 1.28 +seq), calls a function on every element, and uses the return value 1.29 +of the function in place of the original. This makes it fairly 1.30 +easy to write recursive search-and-replace functions, as shown in 1.31 +the examples. 1.32 + 1.33 +Note: \"walk\" supports all Clojure data structures EXCEPT maps 1.34 +created with sorted-map-by. There is no (obvious) way to retrieve 1.35 +the sorting function."} 1.36 + clojure.walk) 1.37 + 1.38 +(defn walk 1.39 + "Traverses form, an arbitrary data structure. inner and outer are 1.40 + functions. Applies inner to each element of form, building up a 1.41 + data structure of the same type, then applies outer to the result. 1.42 + Recognizes all Clojure data structures except sorted-map-by. 1.43 + Consumes seqs as with doall." 1.44 + {:added "1.1"} 1.45 + [inner outer form] 1.46 + (cond 1.47 + (list? form) (outer (apply list (map inner form))) 1.48 + (seq? form) (outer (doall (map inner form))) 1.49 + (vector? form) (outer (vec (map inner form))) 1.50 + (map? form) (outer (into (if (sorted? form) (sorted-map) {}) 1.51 + (map inner form))) 1.52 + (set? form) (outer (into (if (sorted? form) (sorted-set) #{}) 1.53 + (map inner form))) 1.54 + :else (outer form))) 1.55 + 1.56 +(defn postwalk 1.57 + "Performs a depth-first, post-order traversal of form. Calls f on 1.58 + each sub-form, uses f's return value in place of the original. 1.59 + Recognizes all Clojure data structures except sorted-map-by. 1.60 + Consumes seqs as with doall." 1.61 + {:added "1.1"} 1.62 + [f form] 1.63 + (walk (partial postwalk f) f form)) 1.64 + 1.65 +(defn prewalk 1.66 + "Like postwalk, but does pre-order traversal." 1.67 + {:added "1.1"} 1.68 + [f form] 1.69 + (walk (partial prewalk f) identity (f form))) 1.70 + 1.71 + 1.72 +;; Note: I wanted to write: 1.73 +;; 1.74 +;; (defn walk 1.75 +;; [f form] 1.76 +;; (let [pf (partial walk f)] 1.77 +;; (if (coll? form) 1.78 +;; (f (into (empty form) (map pf form))) 1.79 +;; (f form)))) 1.80 +;; 1.81 +;; but this throws a ClassCastException when applied to a map. 1.82 + 1.83 + 1.84 +(defn postwalk-demo 1.85 + "Demonstrates the behavior of postwalk by printing each form as it is 1.86 + walked. Returns form." 1.87 + {:added "1.1"} 1.88 + [form] 1.89 + (postwalk (fn [x] (print "Walked: ") (prn x) x) form)) 1.90 + 1.91 +(defn prewalk-demo 1.92 + "Demonstrates the behavior of prewalk by printing each form as it is 1.93 + walked. Returns form." 1.94 + {:added "1.1"} 1.95 + [form] 1.96 + (prewalk (fn [x] (print "Walked: ") (prn x) x) form)) 1.97 + 1.98 +(defn keywordize-keys 1.99 + "Recursively transforms all map keys from strings to keywords." 1.100 + {:added "1.1"} 1.101 + [m] 1.102 + (let [f (fn [[k v]] (if (string? k) [(keyword k) v] [k v]))] 1.103 + ;; only apply to maps 1.104 + (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m))) 1.105 + 1.106 +(defn stringify-keys 1.107 + "Recursively transforms all map keys from keywords to strings." 1.108 + {:added "1.1"} 1.109 + [m] 1.110 + (let [f (fn [[k v]] (if (keyword? k) [(name k) v] [k v]))] 1.111 + ;; only apply to maps 1.112 + (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m))) 1.113 + 1.114 +(defn prewalk-replace 1.115 + "Recursively transforms form by replacing keys in smap with their 1.116 + values. Like clojure/replace but works on any data structure. Does 1.117 + replacement at the root of the tree first." 1.118 + {:added "1.1"} 1.119 + [smap form] 1.120 + (prewalk (fn [x] (if (contains? smap x) (smap x) x)) form)) 1.121 + 1.122 +(defn postwalk-replace 1.123 + "Recursively transforms form by replacing keys in smap with their 1.124 + values. Like clojure/replace but works on any data structure. Does 1.125 + replacement at the leaves of the tree first." 1.126 + {:added "1.1"} 1.127 + [smap form] 1.128 + (postwalk (fn [x] (if (contains? smap x) (smap x) x)) form)) 1.129 + 1.130 +(defn macroexpand-all 1.131 + "Recursively performs all possible macroexpansions in form." 1.132 + {:added "1.1"} 1.133 + [form] 1.134 + (prewalk (fn [x] (if (seq? x) (macroexpand x) x)) form)) 1.135 +