From cb0c2a1c65bcc3a19e055f5ea69699f58a147be5 Mon Sep 17 00:00:00 2001 From: Fogus Date: Fri, 16 Jan 2026 09:55:01 -0500 Subject: [PATCH] CLJ-2802: Added stateful string accumulator for use in a transduce/reduce that uses StringBuilder as its intermediate state. --- src/clj/clojure/core.clj | 14 ++++++++++++++ test/clojure/test_clojure/transducers.clj | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index be8e61c949..8949545994 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -7026,6 +7026,20 @@ fails, attempts to require sym's namespace and retries." (clojure.core.protocols/coll-reduce coll f init))] (f ret)))) +(defn str! + "Reducing function for efficiently building a string in linear + time within a reduce/transduce call, avoiding the overhead of + repeated intermediate string creation and copying that can occur + when using clojure.core/str." + {:added "1.13"} + (^StringBuilder [] (StringBuilder.)) + (^String [sb] (str sb)) + (^StringBuilder [maybe-sb x] + (let [^StringBuilder sb (if (instance? StringBuilder maybe-sb) + maybe-sb + (StringBuilder. ^CharSequence maybe-sb))] + (.append sb x)))) + (defn into "Returns a new coll consisting of to with all of the items of from conjoined. A transducer may be supplied. diff --git a/test/clojure/test_clojure/transducers.clj b/test/clojure/test_clojure/transducers.clj index b7a9c665e2..15913fb177 100644 --- a/test/clojure/test_clojure/transducers.clj +++ b/test/clojure/test_clojure/transducers.clj @@ -408,3 +408,22 @@ (halt-when :anomaly #(assoc %2 :partial-results %1)) [1 2 {:anomaly :oh-no!} 3 4])))) +(deftest test-str! + (is (= "0123456789" + (transduce identity str! (range 0 10)))) + + (is (= "0123456789" + (transduce identity (completing str! str) (range 0 10)))) + + (is (instance? StringBuilder (str!))) + + (let [^StringBuilder interim-sb1 (reduce str! "A" (range 0 10)) + ^StringBuilder interim-sb2 (reduce str! (StringBuffer. "A") (range 0 10))] + (is (= "A0123456789" (str! interim-sb1))) + (is (= "A0123456789" (str! interim-sb2)))) + + (is (= "0123456789,0123456789" + (transduce (interpose \,) + str! + (map #(reduce str! (str!) %) + [(range 0 10) (range 0 10)])))))