Mercurial > lasercutter
comparison src/clojure/string.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 ; Copyright (c) Rich Hickey. All rights reserved. | |
2 ; The use and distribution terms for this software are covered by the | |
3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) | |
4 ; which can be found in the file epl-v10.html at the root of this distribution. | |
5 ; By using this software in any fashion, you are agreeing to be bound by | |
6 ; the terms of this license. | |
7 ; You must not remove this notice, or any other, from this software. | |
8 | |
9 (ns ^{:doc "Clojure String utilities | |
10 | |
11 It is poor form to (:use clojure.string). Instead, use require | |
12 with :as to specify a prefix, e.g. | |
13 | |
14 (ns your.namespace.here | |
15 (:require '[clojure.string :as str])) | |
16 | |
17 Design notes for clojure.string: | |
18 | |
19 1. Strings are objects (as opposed to sequences). As such, the | |
20 string being manipulated is the first argument to a function; | |
21 passing nil will result in a NullPointerException unless | |
22 documented otherwise. If you want sequence-y behavior instead, | |
23 use a sequence. | |
24 | |
25 2. Functions are generally not lazy, and call straight to host | |
26 methods where those are available and efficient. | |
27 | |
28 3. Functions take advantage of String implementation details to | |
29 write high-performing loop/recurs instead of using higher-order | |
30 functions. (This is not idiomatic in general-purpose application | |
31 code.) | |
32 | |
33 4. When a function is documented to accept a string argument, it | |
34 will take any implementation of the correct *interface* on the | |
35 host platform. In Java, this is CharSequence, which is more | |
36 general than String. In ordinary usage you will almost always | |
37 pass concrete strings. If you are doing something unusual, | |
38 e.g. passing a mutable implementation of CharSequence, then | |
39 thead-safety is your responsibility." | |
40 :author "Stuart Sierra, Stuart Halloway, David Liebke"} | |
41 clojure.string | |
42 (:refer-clojure :exclude (replace reverse)) | |
43 (:import (java.util.regex Pattern) | |
44 clojure.lang.LazilyPersistentVector)) | |
45 | |
46 (defn ^String reverse | |
47 "Returns s with its characters reversed." | |
48 {:added "1.2"} | |
49 [^CharSequence s] | |
50 (.toString (.reverse (StringBuilder. s)))) | |
51 | |
52 (defn- replace-by | |
53 [^CharSequence s re f] | |
54 (let [m (re-matcher re s)] | |
55 (let [buffer (StringBuffer. (.length s))] | |
56 (loop [] | |
57 (if (.find m) | |
58 (do (.appendReplacement m buffer (f (re-groups m))) | |
59 (recur)) | |
60 (do (.appendTail m buffer) | |
61 (.toString buffer))))))) | |
62 | |
63 (defn ^String replace | |
64 "Replaces all instance of match with replacement in s. | |
65 | |
66 match/replacement can be: | |
67 | |
68 string / string | |
69 char / char | |
70 pattern / (string or function of match). | |
71 | |
72 See also replace-first." | |
73 {:added "1.2"} | |
74 [^CharSequence s match replacement] | |
75 (let [s (.toString s)] | |
76 (cond | |
77 (instance? Character match) (.replace s ^Character match ^Character replacement) | |
78 (instance? CharSequence match) (.replace s ^CharSequence match ^CharSequence replacement) | |
79 (instance? Pattern match) (if (instance? CharSequence replacement) | |
80 (.replaceAll (re-matcher ^Pattern match s) | |
81 (.toString ^CharSequence replacement)) | |
82 (replace-by s match replacement)) | |
83 :else (throw (IllegalArgumentException. (str "Invalid match arg: " match)))))) | |
84 | |
85 (defn- replace-first-by | |
86 [^CharSequence s ^Pattern re f] | |
87 (let [m (re-matcher re s)] | |
88 (let [buffer (StringBuffer. (.length s))] | |
89 (if (.find m) | |
90 (let [rep (f (re-groups m))] | |
91 (.appendReplacement m buffer rep) | |
92 (.appendTail m buffer) | |
93 (str buffer)))))) | |
94 | |
95 (defn- replace-first-char | |
96 [^CharSequence s ^Character match replace] | |
97 (let [s (.toString s) | |
98 i (.indexOf s (int match))] | |
99 (if (= -1 i) | |
100 s | |
101 (str (subs s 0 i) replace (subs s (inc i)))))) | |
102 | |
103 (defn ^String replace-first | |
104 "Replaces the first instance of match with replacement in s. | |
105 | |
106 match/replacement can be: | |
107 | |
108 char / char | |
109 string / string | |
110 pattern / (string or function of match). | |
111 | |
112 See also replace-all." | |
113 {:added "1.2"} | |
114 [^CharSequence s match replacement] | |
115 (let [s (.toString s)] | |
116 (cond | |
117 (instance? Character match) | |
118 (replace-first-char s match replacement) | |
119 (instance? CharSequence match) | |
120 (.replaceFirst s (Pattern/quote (.toString ^CharSequence match)) | |
121 (.toString ^CharSequence replacement)) | |
122 (instance? Pattern match) | |
123 (if (instance? CharSequence replacement) | |
124 (.replaceFirst (re-matcher ^Pattern match s) | |
125 (.toString ^CharSequence replacement)) | |
126 (replace-first-by s match replacement)) | |
127 :else (throw (IllegalArgumentException. (str "Invalid match arg: " match)))))) | |
128 | |
129 | |
130 (defn ^String join | |
131 "Returns a string of all elements in coll, separated by | |
132 an optional separator. Like Perl's join." | |
133 {:added "1.2"} | |
134 ([coll] | |
135 (apply str coll)) | |
136 ([separator [x & more]] | |
137 (loop [sb (StringBuilder. (str x)) | |
138 more more | |
139 sep (str separator)] | |
140 (if more | |
141 (recur (-> sb (.append sep) (.append (str (first more)))) | |
142 (next more) | |
143 sep) | |
144 (str sb))))) | |
145 | |
146 (defn ^String capitalize | |
147 "Converts first character of the string to upper-case, all other | |
148 characters to lower-case." | |
149 {:added "1.2"} | |
150 [^CharSequence s] | |
151 (let [s (.toString s)] | |
152 (if (< (count s) 2) | |
153 (.toUpperCase s) | |
154 (str (.toUpperCase (subs s 0 1)) | |
155 (.toLowerCase (subs s 1)))))) | |
156 | |
157 (defn ^String upper-case | |
158 "Converts string to all upper-case." | |
159 {:added "1.2"} | |
160 [^CharSequence s] | |
161 (.. s toString toUpperCase)) | |
162 | |
163 (defn ^String lower-case | |
164 "Converts string to all lower-case." | |
165 {:added "1.2"} | |
166 [^CharSequence s] | |
167 (.. s toString toLowerCase)) | |
168 | |
169 (defn split | |
170 "Splits string on a regular expression. Optional argument limit is | |
171 the maximum number of splits. Not lazy. Returns vector of the splits." | |
172 {:added "1.2"} | |
173 ([^CharSequence s ^Pattern re] | |
174 (LazilyPersistentVector/createOwning (.split re s))) | |
175 ([ ^CharSequence s ^Pattern re limit] | |
176 (LazilyPersistentVector/createOwning (.split re s limit)))) | |
177 | |
178 (defn split-lines | |
179 "Splits s on \\n or \\r\\n." | |
180 {:added "1.2"} | |
181 [^CharSequence s] | |
182 (split s #"\r?\n")) | |
183 | |
184 (defn ^String trim | |
185 "Removes whitespace from both ends of string." | |
186 {:added "1.2"} | |
187 [^CharSequence s] | |
188 (.. s toString trim)) | |
189 | |
190 (defn ^String triml | |
191 "Removes whitespace from the left side of string." | |
192 {:added "1.2"} | |
193 [^CharSequence s] | |
194 (loop [index (int 0)] | |
195 (if (= (.length s) index) | |
196 "" | |
197 (if (Character/isWhitespace (.charAt s index)) | |
198 (recur (inc index)) | |
199 (.. s (subSequence index (.length s)) toString))))) | |
200 | |
201 (defn ^String trimr | |
202 "Removes whitespace from the right side of string." | |
203 {:added "1.2"} | |
204 [^CharSequence s] | |
205 (loop [index (.length s)] | |
206 (if (zero? index) | |
207 "" | |
208 (if (Character/isWhitespace (.charAt s (dec index))) | |
209 (recur (dec index)) | |
210 (.. s (subSequence 0 index) toString))))) | |
211 | |
212 (defn ^String trim-newline | |
213 "Removes all trailing newline \\n or return \\r characters from | |
214 string. Similar to Perl's chomp." | |
215 {:added "1.2"} | |
216 [^CharSequence s] | |
217 (loop [index (.length s)] | |
218 (if (zero? index) | |
219 "" | |
220 (let [ch (.charAt s (dec index))] | |
221 (if (or (= ch \newline) (= ch \return)) | |
222 (recur (dec index)) | |
223 (.. s (subSequence 0 index) toString)))))) | |
224 | |
225 (defn blank? | |
226 "True if s is nil, empty, or contains only whitespace." | |
227 {:added "1.2"} | |
228 [^CharSequence s] | |
229 (if s | |
230 (loop [index (int 0)] | |
231 (if (= (.length s) index) | |
232 true | |
233 (if (Character/isWhitespace (.charAt s index)) | |
234 (recur (inc index)) | |
235 false))) | |
236 true)) | |
237 | |
238 (defn ^String escape | |
239 "Return a new string, using cmap to escape each character ch | |
240 from s as follows: | |
241 | |
242 If (cmap ch) is nil, append ch to the new string. | |
243 If (cmap ch) is non-nil, append (str (cmap ch)) instead." | |
244 {:added "1.2"} | |
245 [^CharSequence s cmap] | |
246 (loop [index (int 0) | |
247 buffer (StringBuilder. (.length s))] | |
248 (if (= (.length s) index) | |
249 (.toString buffer) | |
250 (let [ch (.charAt s index)] | |
251 (if-let [replacement (cmap ch)] | |
252 (.append buffer replacement) | |
253 (.append buffer ch)) | |
254 (recur (inc index) buffer))))) |