Adding new subseq-pred-fn macro to create the new form of predicate
function (taking the previous and current item), to preserve backward compatibility (still allowing predicate functions that only take the current item). This macro also takes in a get-truthy-fn as its first argument, which is a function that marks whether that item in the sequence should be included in a subsequence. This is necessary because the predicate function can now be of any arbitrary form, so we cannot make any assumption about how the user intends for that result to be interpreted as a "filter". Adding SubseqsDynamicPredFn, which works the same as SrangeEndFn, to support backward compatibility Adding wrapper to take a predicate on [prev current] and turn it into a predicate also taking the current index as the first param Creating transducer to combine this with the user-supplied predicate function Adding tests for select and transform TODO: figure out how to make predicate function handle an open-ended subsequence (ex: end marker not yet seen)
This commit is contained in:
parent
a379893598
commit
0ddebad851
5 changed files with 80 additions and 9 deletions
|
|
@ -7,6 +7,5 @@ lein do clean, test
|
||||||
# Running ClojureScript tests
|
# Running ClojureScript tests
|
||||||
|
|
||||||
```
|
```
|
||||||
lein javac
|
lein do clean, javac, test-cljs
|
||||||
lein doo node test-build once
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -40,4 +40,5 @@
|
||||||
[["clojars" {:url "https://repo.clojars.org"
|
[["clojars" {:url "https://repo.clojars.org"
|
||||||
:sign-releases false}]]
|
:sign-releases false}]]
|
||||||
|
|
||||||
:aliases {"deploy" ["do" "clean," "deploy" "clojars"]})
|
:aliases {"deploy" ["do" "clean," "deploy" "clojars"]
|
||||||
|
"test-cljs" ["do" "doo" "node" "test-build" "once"]})
|
||||||
|
|
|
||||||
|
|
@ -507,6 +507,11 @@
|
||||||
(defmacro end-fn [& args]
|
(defmacro end-fn [& args]
|
||||||
`(n/->SrangeEndFunction (fn ~@args)))
|
`(n/->SrangeEndFunction (fn ~@args)))
|
||||||
|
|
||||||
|
(defmacro subseq-pred-fn
|
||||||
|
"Used in conjunction with `continuous-subseqs`. See [[continuous-subseqs]]."
|
||||||
|
[get-truthy-fn & args]
|
||||||
|
`(i/->SubseqsDynamicPredFn ~get-truthy-fn (i/wrap-pred-with-index (fn ~@args))))
|
||||||
|
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -800,7 +805,14 @@
|
||||||
|
|
||||||
|
|
||||||
(defnav
|
(defnav
|
||||||
^{:doc "Navigates to every continuous subsequence of elements matching `pred`"}
|
^{:doc "Navigates to every continuous subsequence of elements matching `pred`. `pred` can be specified one of two
|
||||||
|
forms. If a regular function (e.g. defined with `fn`), it takes in only the current element as input. If
|
||||||
|
defined using the special `subseq-pred-fn` macro, it takes in a `get-truthy-fn` as its first parameter,
|
||||||
|
followed by arguments to a predicate function [`elem` `prev`], followed by the predicate function body. The
|
||||||
|
`elem` argument to the predicate function is the current element, and the `pred` argument is the value
|
||||||
|
returned by your predicate on the previous element, so it can be in any structure you choose. `get-truthy-fn`
|
||||||
|
is a function that should return true from your predicate's return structure if that element should be
|
||||||
|
included in a subsequence."}
|
||||||
continuous-subseqs
|
continuous-subseqs
|
||||||
[pred]
|
[pred]
|
||||||
(select* [this structure next-fn]
|
(select* [this structure next-fn]
|
||||||
|
|
|
||||||
|
|
@ -546,8 +546,38 @@
|
||||||
res
|
res
|
||||||
))))
|
))))
|
||||||
|
|
||||||
|
(defn wrap-pred-with-index [pred]
|
||||||
|
(fn [i elem prev]
|
||||||
|
[(pred elem (first prev)), i]))
|
||||||
|
|
||||||
|
;; adapted from clojure.core$keep_indexed
|
||||||
|
(defn- subseq-pred-fn-transducer
|
||||||
|
([pred-fn]
|
||||||
|
(fn [rf]
|
||||||
|
(let [last-val (volatile! nil) idx (volatile! -1)]
|
||||||
|
(fn
|
||||||
|
([] (rf)) ;; init arity
|
||||||
|
([result] (rf result)) ;; completion arity
|
||||||
|
([result input] ;; reduction arity
|
||||||
|
(let [last @last-val
|
||||||
|
i (vswap! idx inc)
|
||||||
|
curr ((:pred-fn pred-fn) i input last)]
|
||||||
|
(vreset! last-val curr)
|
||||||
|
(if (nil? curr)
|
||||||
|
result
|
||||||
|
(rf result curr)))))))))
|
||||||
|
|
||||||
|
;; see com.rpl.specter.navs.SrangeEndFunction
|
||||||
|
(defrecord SubseqsDynamicPredFn [get-truthy-fn pred-fn])
|
||||||
|
|
||||||
(defn- matching-indices [aseq p]
|
(defn- matching-indices [aseq p]
|
||||||
(keep-indexed (fn [i e] (if (p e) i)) aseq))
|
(if (instance? SubseqsDynamicPredFn p)
|
||||||
|
;; use new subseq predicate form (taking current and previous vals)
|
||||||
|
(let [index-results (into [] (subseq-pred-fn-transducer p) aseq)]
|
||||||
|
;; apply the get-truthy-fn to extract the truthy (i.e. include) result
|
||||||
|
(map last (filter (comp true? (:get-truthy-fn p) first) index-results)))
|
||||||
|
;; else use the previous 1-arity predicate
|
||||||
|
(keep-indexed (fn [i e] (if (p e) i)) aseq)))
|
||||||
|
|
||||||
(defn matching-ranges [aseq p]
|
(defn matching-ranges [aseq p]
|
||||||
(first
|
(first
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
select-any selected-any? collected? traverse
|
select-any selected-any? collected? traverse
|
||||||
multi-transform path dynamicnav recursive-path
|
multi-transform path dynamicnav recursive-path
|
||||||
defdynamicnav traverse-all satisfies-protpath? end-fn
|
defdynamicnav traverse-all satisfies-protpath? end-fn
|
||||||
vtransform]]))
|
subseq-pred-fn vtransform]]))
|
||||||
(:use
|
(:use
|
||||||
#?(:clj [clojure.test :only [deftest is]])
|
#?(:clj [clojure.test :only [deftest is]])
|
||||||
#?(:clj [clojure.test.check.clojure-test :only [defspec]])
|
#?(:clj [clojure.test.check.clojure-test :only [defspec]])
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
select-any selected-any? collected? traverse
|
select-any selected-any? collected? traverse
|
||||||
multi-transform path dynamicnav recursive-path
|
multi-transform path dynamicnav recursive-path
|
||||||
defdynamicnav traverse-all satisfies-protpath? end-fn
|
defdynamicnav traverse-all satisfies-protpath? end-fn
|
||||||
vtransform]]))
|
subseq-pred-fn vtransform]]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -949,6 +949,23 @@
|
||||||
(= (setval (s/continuous-subseqs pred) nil aseq)
|
(= (setval (s/continuous-subseqs pred) nil aseq)
|
||||||
(filter (complement pred) aseq))))
|
(filter (complement pred) aseq))))
|
||||||
|
|
||||||
|
(defn- make-bounds-pred-fn-vecs [start end]
|
||||||
|
(s/subseq-pred-fn first [elem prev]
|
||||||
|
(let [[included last] prev]
|
||||||
|
(cond
|
||||||
|
(= start elem) [false start]
|
||||||
|
(= end elem) [false end]
|
||||||
|
(= end last) [false elem]
|
||||||
|
:else [(or (= start last) included) elem]))))
|
||||||
|
|
||||||
|
(defn- make-bounds-pred-fn-maps [start end]
|
||||||
|
(s/subseq-pred-fn :include [elem prev]
|
||||||
|
(let [{include :include last :last} prev]
|
||||||
|
(cond
|
||||||
|
(= start elem) {:include false :last start}
|
||||||
|
(= end elem) {:include false :last end}
|
||||||
|
(= end last) {:include false :last elem}
|
||||||
|
:else {:include (or (= start last) include) :last elem}))))
|
||||||
|
|
||||||
(deftest continuous-subseqs-test
|
(deftest continuous-subseqs-test
|
||||||
(is (= [1 "ab" 2 3 "c" 4 "def"]
|
(is (= [1 "ab" 2 3 "c" 4 "def"]
|
||||||
|
|
@ -960,7 +977,19 @@
|
||||||
(is (= [[] [2] [4 6]]
|
(is (= [[] [2] [4 6]]
|
||||||
(select
|
(select
|
||||||
[(s/continuous-subseqs number?) (s/filterer even?)]
|
[(s/continuous-subseqs number?) (s/filterer even?)]
|
||||||
[1 "a" "b" 2 3 "c" 4 5 6 "d" "e" "f"]))))
|
[1 "a" "b" 2 3 "c" 4 5 6 "d" "e" "f"])))
|
||||||
|
(is (= [[1 2 3] [8 9]]
|
||||||
|
(select
|
||||||
|
[(s/continuous-subseqs (make-bounds-pred-fn-vecs :START :END))]
|
||||||
|
[:START 1 2 3 :END 5 6 7 :START 8 9 :END])))
|
||||||
|
|
||||||
|
(is (= [1 2 3 :START-SUM 15 :END-SUM 7 8 9 :START-SUM 21 :END-SUM 12 :START-SUM 27 :END-SUM]
|
||||||
|
(transform
|
||||||
|
(s/continuous-subseqs (make-bounds-pred-fn-maps :START-SUM :END-SUM))
|
||||||
|
(fn [vals] [(apply + vals)])
|
||||||
|
[1 2 3 :START-SUM 4 5 6 :END-SUM 7 8 9 :START-SUM 10 11 :END-SUM 12 :START-SUM 13 14 :END-SUM])))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1589,7 +1618,7 @@
|
||||||
(s/comp-paths
|
(s/comp-paths
|
||||||
(s/srange-dynamic
|
(s/srange-dynamic
|
||||||
(fn [aseq] (long (/ (count aseq) 2)))
|
(fn [aseq] (long (/ (count aseq) 2)))
|
||||||
(end-fn [aseq s] (if (empty? aseq) 0 (inc s))))
|
(s/end-fn [aseq s] (if (empty? aseq) 0 (inc s))))
|
||||||
s/FIRST
|
s/FIRST
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue