Last active
April 25, 2021 13:09
-
-
Save holyjak/578571a134ce90526e6907436e91014a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn stateful-map | |
"Returns a stateful transducer similar to `clojure.core/map-indexed` that | |
transforms each item with `(f state <the item>)` where `state` is built by | |
applying `state-building-fn` to the previous state (starting with `init`) and | |
the transformed item. | |
Ex., re-implementing map-indexed: | |
```clojure | |
(sequence (stateful-map vector (fn build-state [idx _] (if idx (inc idx) 5))) \"abc\") | |
; => ([nil \\a] [5 \\b] [6 \\c]) | |
(sequence (stateful-map (fn stateful-map [seen n] (if (seen n) :duplicate n)) conj #{}) | |
[1 2 1 3 4 2 2 5]) | |
; => (1 2 :duplicate 3 4 :duplicate :duplicate 5) | |
``` | |
See also [[stateful-filter]]." | |
([f state-building-fn] (stateful-map f state-building-fn nil)) | |
([f state-building-fn init] | |
(fn [rf] | |
(let [state (volatile! init)] | |
(fn | |
([] (rf)) | |
([result] (vreset! state nil) (rf result)) | |
([result input] | |
(let [out (f @state input)] | |
(vswap! state state-building-fn out) | |
(rf result out)))))))) | |
(defn stateful-filter | |
"A transducer similar to `(filter pred)` but the `pred`-icate is invoked with | |
a state and the current item, where state is built from previously filtered | |
items (i.e from the previous state [starting from `init`] and the | |
current item) by the `state-building-fn`. | |
Ex.: | |
```clojure | |
(sequence (stateful-filter not= (fn build-state [_ prev] prev)) [1 2 2 2 3 4 4 5]) | |
; => (1 2 3 4 5) | |
;; Ensure an increasing seq: | |
(sequence (stateful-filter < max -1) (shuffle (range 20))) | |
; => (7 9 10 12 16 17 19) | |
See also [[stateful-map]]. | |
```" | |
([pred state-building-fn] (stateful-filter pred state-building-fn nil)) | |
([pred state-building-fn init] | |
(fn [rf] | |
(let [state (volatile! init)] | |
(fn | |
([] (rf)) | |
([result] (vreset! state nil) (rf result)) | |
([result input] | |
(if (pred @state input) | |
(do | |
(vswap! state state-building-fn input) | |
(rf result input)) | |
result))))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
As discovered by others, we could replace the
volatile!
with the thread-unsafe (which is OK since it will ever only be accessed by a single thread) but faster mutable var: