diff --git a/CHANGES.md b/CHANGES.md index f20b99c..71152b0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ * Huge performance improvement for ALL transform on maps and vectors * Significant performance improvements for FIRST/LAST for vectors * Huge performance improvements for `if-path`, `cond-path`, `selected?`, and `not-selected?`, especially for condition path containing only static functions +* Huge performance improvement for `END` on vectors * Added specialized MAP-VALS navigator that is twice as fast as using [ALL LAST] * Eliminated compiler warnings for ClojureScript version * Dropped support for Clojurescript below v1.7.10 diff --git a/project.clj b/project.clj index 9f2ab74..a646ba8 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,8 @@ :codox {:source-paths ["target/classes" "src/clj"] :namespaces [com.rpl.specter com.rpl.specter.macros - com.rpl.specter.zipper] + com.rpl.specter.zipper + com.rpl.specter.protocols] :source-uri {#"target/classes" "https://github.com/nathanmarz/specter/tree/{version}/src/clj/{classpath}x#L{line}" #".*" "https://github.com/nathanmarz/specter/tree/{version}/src/clj/{classpath}#L{line}" diff --git a/scripts/benchmarks.clj b/scripts/benchmarks.clj index 2ad07f1..939f1a3 100644 --- a/scripts/benchmarks.clj +++ b/scripts/benchmarks.clj @@ -51,7 +51,6 @@ (println "\n********************************\n") ))) - (let [data {:a {:b {:c 1}}} p (comp-paths :a :b :c)] (run-benchmark "get value in nested map" 10000000 @@ -104,6 +103,12 @@ (->> data (mapv :a) (filter even?) doall) )) +(let [v (vec (range 1000))] + (run-benchmark "END on large vector" + 5000000 + (setval END [1] v) + (reduce conj v [1]) + (conj v 1))) (defn- update-pair [[k v]] [k (inc v)]) diff --git a/src/clj/com/rpl/specter.cljx b/src/clj/com/rpl/specter.cljx index b205630..b3c93ba 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -252,16 +252,28 @@ (reverse (i/matching-ranges structure pred)) ))) -(def +(defnav ^{:doc "Navigate to the empty subsequence before the first element of the collection."} BEGINNING - (srange 0 0)) + [] + (select* [this structure next-fn] + (next-fn [])) + (transform* [this structure next-fn] + (let [to-prepend (next-fn [])] + (i/prepend-all structure to-prepend) + ))) -(def +(defnav ^{:doc "Navigate to the empty subsequence after the last element of the collection."} END - (srange-dynamic count count)) - + [] + (select* [this structure next-fn] + (next-fn [])) + (transform* [this structure next-fn] + (let [to-append (next-fn [])] + (i/append-all structure to-append) + ))) + (defnav ^{:doc "Navigates to the specified subset (by taking an intersection). In a transform, that subset in the original set is changed to the diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index d9ed666..f69998e 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -474,6 +474,30 @@ (defn- append [coll elem] (-> coll vec (conj elem))) +(defprotocol AddExtremes + (append-all [structure elements]) + (prepend-all [structure elements])) + +(extend-protocol AddExtremes + #+clj clojure.lang.PersistentVector #+cljs cljs.core/PersistentVector + (append-all [structure elements] + (reduce conj structure elements)) + (prepend-all [structure elements] + (let [ret (transient [])] + (as-> ret <> + (reduce conj! <> elements) + (reduce conj! <> structure) + (persistent! <>) + ))) + + #+clj Object #+cljs default + (append-all [structure elements] + (concat structure elements)) + (prepend-all [structure elements] + (concat elements structure)) + ) + + (defprotocol UpdateExtremes (update-first [s afn]) (update-last [s afn]))