From f605167a53e829e51983b3fdacac45435c9af8cf Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Thu, 9 Jun 2016 16:36:20 -0400 Subject: [PATCH] implement traverse --- scripts/benchmarks.clj | 10 ++++++++++ src/clj/com/rpl/specter.cljx | 11 +++++++++++ src/clj/com/rpl/specter/impl.cljx | 14 ++++++++++++++ src/clj/com/rpl/specter/macros.clj | 9 +++++++++ 4 files changed, 44 insertions(+) diff --git a/scripts/benchmarks.clj b/scripts/benchmarks.clj index cf80a41..7dc0450 100644 --- a/scripts/benchmarks.clj +++ b/scripts/benchmarks.clj @@ -52,6 +52,15 @@ (println "\n********************************\n") ))) +(let [data (range 1000)] + (run-benchmark "Traverse into a set" 5000 + (set data) + (set (select ALL data)) + (persistent! + (reduce conj! (transient #{}) (traverse ALL data))) + (reduce conj #{} (traverse ALL data)) + )) + (let [data {:a {:b {:c 1}}} p (comp-paths :a :b :c)] (run-benchmark "get value in nested map" 5000000 @@ -243,3 +252,4 @@ 2000000 (vary-meta data assoc :y 2) (setval [META :y] 2 data))) + diff --git a/src/clj/com/rpl/specter.cljx b/src/clj/com/rpl/specter.cljx index 9d185ac..f7b60da 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -104,6 +104,17 @@ [path structure] (compiled-selected-any? (i/comp-paths* path) structure)) +;; Reducible traverse functions + +(def ^{:doc "Version of traverse that takes in a path pre-compiled with comp-paths"} + compiled-traverse i/do-compiled-traverse) + +(defn traverse* + "Return a reducible object that traverses over `structure` to every element + specified by the path" + [apath structure] + (compiled-traverse (i/comp-paths* apath) structure)) + ;; Transformation functions (def ^{:doc "Version of transform that takes in a path pre-compiled with comp-paths"} diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index ffddf99..5ac024c 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -653,6 +653,20 @@ structure) )) +(defn do-compiled-traverse [apath structure] + (reify clojure.lang.IReduceInit + (reduce [this afn start] + (let [cell (mutable-cell start)] + (compiled-traverse* + apath + (fn [elem] + (let [curr (get-cell cell)] + (set-cell! cell (afn curr elem)) + )) + structure + ) + (get-cell cell) + )))) (defn compiled-select* [path structure] (let [res (mutable-cell (transient [])) diff --git a/src/clj/com/rpl/specter/macros.clj b/src/clj/com/rpl/specter/macros.clj index c87a29e..a5d757f 100644 --- a/src/clj/com/rpl/specter/macros.clj +++ b/src/clj/com/rpl/specter/macros.clj @@ -599,6 +599,15 @@ [apath aval structure] `(i/compiled-setval* (path ~apath) ~aval ~structure)) +(defmacro traverse + "Return a reducible object that traverses over `structure` to every element + specified by the path. + This macro will attempt to do inline factoring and caching of the path, falling + back to compiling the path on every invocation it it's not possible to + factor/cache the path." + [apath structure] + `(i/do-compiled-traverse (path ~apath) ~structure)) + (defmacro replace-in "Similar to transform, except returns a pair of [transformed-structure sequence-of-user-ret]. The transform-fn in this case is expected to return [ret user-ret]. ret is