In my previous post, I showed a simple (naive) algorithm for doing a String template replacement.
One of the solutions, provided by mikera, seems like a much better algo
I think you are hit by reflection. *warn-on-reflection*
is your friend. Here some tests with criterium.
replace-templates-original: 56.4us
replace-templates-original-hinted: 9.4us
replace-templates-new: 131.4us
replace-templates-new-hinted: 6.3us
replace-templates-very-new: 7.3us
Here is the replace-templates-very-new
, a version I did myself for golf. :)
(defn replace-templates-very-new
[^String text m]
(let [builder (StringBuilder.)]
(loop [text text]
(cond
(zero? (count text))
(.toString builder)
(.startsWith text "{")
(let [brace (.indexOf text "}")]
(if (neg? brace)
(.toString (.append builder text))
(do
(.append builder (get m (keyword (subs text 1 brace))))
(recur (subs text (inc brace))))))
:else
(let [brace (.indexOf text "{")]
(if (neg? brace)
(.toString (.append builder text))
(do
(.append builder (subs text 0 brace))
(recur (subs text brace)))))))))
It passes all tests, so it should work.
UPDATE: Support non-key brace enclosed values ("this is a {not-a-key-{foo}-in-the-map} test" => "this is a {not-a-key-FOO-in-the-map} test"
), allowing it to be used in a Java code generator where non-key brace-enclosed things are significant :-).
(defn replace-templates-even-newer
"Return a String with each occurrence of a substring of the form {key}
replaced with the corresponding value from a map parameter.
@param str the String in which to do the replacements
@param m a map of keyword->value
@thanks kotarak http://stackoverflow.com/questions/6112534/
follow-up-to-simple-string-template-replacement-in-scala-and-clojure"
[^String text m]
(let [builder (StringBuilder.)]
(loop [text text]
(cond
(zero? (count text))
(.toString builder)
(.startsWith text "{")
(let [brace (.indexOf text "}")]
(if (neg? brace)
(.toString (.append builder text))
(if-let [[_ replacement] (find m (keyword (subs text 1 brace)))]
(do
(.append builder replacement)
(recur (subs text (inc brace))))
(do
(.append builder "{")
(recur (subs text 1))))))
:else
(let [brace (.indexOf text "{")]
(if (neg? brace)
(.toString (.append builder text))
(do
(.append builder (subs text 0 brace))
(recur (subs text brace)))))))))