Follow-up to “Simple String template replacement in Scala and Clojure”

后端 未结 3 1799
臣服心动
臣服心动 2021-01-22 00:27

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

3条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2021-01-22 01:14

    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)))))))))
    

提交回复
热议问题