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/scripts/benchmarks.clj b/scripts/benchmarks.clj index e93357e..35484b0 100644 --- a/scripts/benchmarks.clj +++ b/scripts/benchmarks.clj @@ -49,8 +49,6 @@ (println "\n********************************\n") ))) - - (let [data {:a {:b {:c 1}}} p (comp-paths :a :b :c)] (run-benchmark "get value in nested map" 10000000 @@ -87,6 +85,12 @@ (transform ALL inc data) )) +(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 dc794c9..d55b8e9 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -229,16 +229,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 c1db2ed..b401e79 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -448,6 +448,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]))