From ff03dbc834dc06cab108eb82b6d48c2b7c4004c5 Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Thu, 9 Jun 2016 16:36:20 -0400 Subject: [PATCH 1/3] 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 56aa909..00e6da7 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -652,6 +652,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 1f2caaa..a0014e3 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 From 02a4bf09ca8889b7127e6d7d8c384763b98dc3a5 Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Thu, 9 Jun 2016 16:43:03 -0400 Subject: [PATCH 2/3] upgrade to clojure 1.7.0 for reducers --- project.clj | 2 +- scripts/benchmarks.clj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 927e9a3..d21da7a 100644 --- a/project.clj +++ b/project.clj @@ -41,7 +41,7 @@ :output-path "target/test-classes" :rules :cljs}]} } - :test {:dependencies [[org.clojure/clojure "1.6.0"]]} + :test {:dependencies [[org.clojure/clojure "1.7.0"]]} } :aliases {"cleantest" ["do" "clean," "cljx" "once," diff --git a/scripts/benchmarks.clj b/scripts/benchmarks.clj index 7dc0450..d7844b9 100644 --- a/scripts/benchmarks.clj +++ b/scripts/benchmarks.clj @@ -56,6 +56,7 @@ (run-benchmark "Traverse into a set" 5000 (set data) (set (select ALL data)) + (into #{} (traverse ALL data)) (persistent! (reduce conj! (transient #{}) (traverse ALL data))) (reduce conj #{} (traverse ALL data)) From e7aae0cb0fc8e025b9745725c570a011fa53c2d5 Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Thu, 9 Jun 2016 17:21:11 -0400 Subject: [PATCH 3/3] add non-init reduce case to traverse --- src/clj/com/rpl/specter/impl.cljx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index 00e6da7..3d48d0b 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -653,7 +653,9 @@ )) (defn do-compiled-traverse [apath structure] - (reify clojure.lang.IReduceInit + (reify clojure.lang.IReduce + (reduce [this afn] + (.reduce this afn (afn))) (reduce [this afn start] (let [cell (mutable-cell start)] (compiled-traverse*