From e2e8fa091ed700d6c8b1b0c025bd2dbf1b7586d4 Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Sat, 28 May 2016 13:57:30 -0400 Subject: [PATCH] Added continuous-subseqs navigator --- CHANGES.md | 2 ++ src/clj/com/rpl/specter.cljx | 18 ++++++++++++++++++ src/clj/com/rpl/specter/impl.cljx | 21 +++++++++++++++++++++ test/com/rpl/specter/core_test.cljx | 22 ++++++++++++++++++++++ 4 files changed, 63 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index d1d1860..99da7b4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,9 +7,11 @@ * BREAKING CHANGE: `path` renamed to `nav` * BREAKING CHANGE: `fixed-pathed-path` and `variable-pathed-path` renamed to `fixed-pathed-nav` and `variabled-pathed-nav` * Added `must` navigator to navigate to a key if and only if it exists in the structure +* Added `continous-subseqs` navigator * Added `ATOM` navigator (thanks @rakeshp) * Added "navigator constructors" that can be defined via `defnavconstructor`. These allow defining a flexible function to parameterize a defnav, and the function integrates with inline caching for high performance. + ## 0.10.0 * Make codebase bootstrap cljs compatible * Remove usage of reducers in cljs version in favor of transducers (thanks @StephenRudolph) diff --git a/src/clj/com/rpl/specter.cljx b/src/clj/com/rpl/specter.cljx index 92425d1..efc476f 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -181,6 +181,24 @@ (i/srange-transform structure start end next-fn) )) +(defnav + ^{:doc "Navigates to every continuous subsequence of elements matching `pred`"} + continuous-subseqs + [pred] + (select* [this structure next-fn] + (doall + (mapcat + (fn [[s e]] (i/srange-select structure s e next-fn)) + (i/matching-ranges structure pred) + ))) + (transform* [this structure next-fn] + (reduce + (fn [structure [s e]] + (i/srange-transform structure s e next-fn)) + structure + (reverse (i/matching-ranges structure pred)) + ))) + (def BEGINNING (srange 0 0)) (def END (srange-dynamic count count)) diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index 65215d9..9da2099 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -575,6 +575,27 @@ res ))) +(defn- matching-indices [aseq p] + (keep-indexed (fn [i e] (if (p e) i)) aseq)) + +(defn matching-ranges [aseq p] + (first + (reduce + (fn [[ranges curr-start curr-last :as curr] i] + (cond + (nil? curr-start) + [ranges i i] + + (= i (inc curr-last)) + [ranges curr-start i] + + :else + [(conj ranges [curr-start (inc curr-last)]) i i] + )) + [[] nil nil] + (concat (matching-indices aseq p) [-1]) + ))) + (extend-protocol p/Navigator nil (select* [this structure next-fn] diff --git a/test/com/rpl/specter/core_test.cljx b/test/com/rpl/specter/core_test.cljx index 87f4f7f..054f723 100644 --- a/test/com/rpl/specter/core_test.cljx +++ b/test/com/rpl/specter/core_test.cljx @@ -969,3 +969,25 @@ :a )))) ) + +(defspec continuous-subseqs-filter-equivalence + (for-all+ + [aseq (gen/vector (gen/elements [1 2 3 :a :b :c 4 5 :d :e])) + pred (gen/elements [keyword? number?])] + (= (setval (s/continuous-subseqs pred) nil aseq) + (filter (complement pred) aseq)) + )) + +(deftest continuous-subseqs-test + (is (= [1 "ab" 2 3 "c" 4 "def"] + (transform + (s/continuous-subseqs string?) + (fn [s] [(apply str s)]) + [1 "a" "b" 2 3 "c" 4 "d" "e" "f"] + ))) + (is (= [[] [2] [4 6]] + (select + [(s/continuous-subseqs number?) (s/filterer even?)] + [1 "a" "b" 2 3 "c" 4 5 6 "d" "e" "f"] + ))) + )