Skip to content

Commit

Permalink
Improve before-index performance redplanetlabs#223
Browse files Browse the repository at this point in the history
Adding new protocol for performing the insert-before-idx operation, with
implementations for core collection types

Adding new functional test to confirm behavior when operating on a string
  • Loading branch information
jeff303 committed Sep 16, 2020
1 parent 40add56 commit 6d14f16
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 6 deletions.
12 changes: 12 additions & 0 deletions scripts/benchmarks.clj
Expand Up @@ -335,3 +335,15 @@
(transform (walker number?) inc data)
(transform (walker-old number?) inc data)
))

(let [size 1000
middle-idx (/ size 2)
v -1
rng (range size)
data-vec (vec rng)
data-lst (apply list rng)]
(run-benchmark "before-index insertion new impl vs. srange and list cons"
(setval (before-index middle-idx) v data-vec)
(setval (srange middle-idx middle-idx) [v] data-vec)
(setval (srange middle-idx middle-idx) [v] data-lst)
(cons v data-lst)))
9 changes: 4 additions & 5 deletions src/clj/com/rpl/specter.cljc
Expand Up @@ -974,11 +974,10 @@
NONE)
(transform* [this vals structure next-fn]
(let [v (next-fn vals NONE)]
(if (identical? NONE v)
structure
;; TODO: make a more efficient impl
(setval (srange index index) [v] structure)
))))
(if
(identical? NONE v)
structure
(n/insert-before-idx structure index v)))))

(defrichnav
^{:doc "Navigates to the index of the sequence if within 0 and size. Transforms move element
Expand Down
32 changes: 32 additions & 0 deletions src/clj/com/rpl/specter/navs.cljc
Expand Up @@ -474,6 +474,9 @@
(defprotocol FastEmpty
(fast-empty? [s]))

(defprotocol InsertBeforeIndex
(insert-before-idx [aseq idx val]))

(defnav PosNavigator [getter updater]
(select* [this structure next-fn]
(if-not (fast-empty? structure)
Expand Down Expand Up @@ -691,3 +694,32 @@
((:end-fn end-fn) structure start)
(end-fn structure)
))

(defn- insert-before-index-list [lst idx val]
;; an implementation that is most efficient for list style structures
(let [[front back] (split-at idx lst)]
(concat front (cons val back))))

(extend-protocol InsertBeforeIndex
nil
(insert-before-idx [_ idx val]
(cond (= 0 idx) [val]
:else (i/throw-illegal "For a nil structure, can only insert before index 0, not at - " idx)))

#?(:clj java.lang.String :cljs string)
(insert-before-idx [aseq idx val]
(apply str (insert-before-index-list aseq idx val)))

#?(:clj clojure.lang.LazySeq :cljs cljs.core/LazySeq)
(insert-before-idx [aseq idx val]
(insert-before-index-list aseq idx val))

#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
(insert-before-idx [aseq idx val]
(let [front (subvec aseq 0 idx)
back (subvec aseq idx)]
(into (conj front val) back)))

#?(:clj clojure.lang.IPersistentList :cljs cljs.core/List)
(insert-before-idx [aseq idx val]
(insert-before-index-list aseq idx val)))
6 changes: 5 additions & 1 deletion test/com/rpl/specter/core_test.cljc
Expand Up @@ -1609,14 +1609,18 @@

(deftest before-index-test
(let [data [1 2 3]
datal '(1 2 3)]
datal '(1 2 3)
data-str "abcdef"]
(is (predand= vector? [:a 1 2 3] (setval (s/before-index 0) :a data)))
(is (predand= vector? [1 2 3] (setval (s/before-index 1) s/NONE data)))
(is (predand= vector? [1 :a 2 3] (setval (s/before-index 1) :a data)))
(is (predand= vector? [1 2 3 :a] (setval (s/before-index 3) :a data)))
; ensure inserting at index 0 in nil structure works, as in previous impl
(is (predand= vector? '[:a] (setval (s/before-index 0) :a nil)))
(is (predand= list? '(:a 1 2 3) (setval (s/before-index 0) :a datal)))
(is (predand= list? '(1 :a 2 3) (setval (s/before-index 1) :a datal)))
(is (predand= list? '(1 2 3 :a) (setval (s/before-index 3) :a datal)))
(is (predand= string? "abcxdef" (setval (s/before-index 3) (char \x) data-str)))
))

(deftest index-nav-test
Expand Down

0 comments on commit 6d14f16

Please sign in to comment.