Skip to content

Instantly share code, notes, and snippets.

@jclaggett
Last active January 22, 2023 00:20
Show Gist options
  • Save jclaggett/fa4f59272e7c5062ba49392210a377c6 to your computer and use it in GitHub Desktop.
Save jclaggett/fa4f59272e7c5062ba49392210a377c6 to your computer and use it in GitHub Desktop.
(ns ezducerdemo)
;;
;; 1st take
;;
(defn ezducer [constructor]
(fn [reducer]
(let [steps (fn [a vs]
;; Use loop/recur instead of reduce to avoid reduce's
;; swallowing the special 'reduced' values.
(loop [a a vs vs]
(if (or (reduced? a) (empty? vs))
a
(if (= (first vs) reduced)
(reduced a)
(recur (reducer a (first vs)) (rest vs))))))
{:keys [step, result]
:or {step (fn [v] [v])
result (fn [] [])}} (constructor)]
(fn
([] (reducer))
([a v] (steps a (step v)))
([a] (reducer (unreduced (steps a (result)))))))))
(defn ezfilter [pred]
(ezducer
(fn []
{:step (fn [v] (if (pred v) [v] []))})))
(def drop-all
(ezducer
(fn []
{:step (fn [_v] [])})))
(defn eztake [n]
(if (< n 1)
drop-all
(ezducer
(fn []
(let [*n* (atom n)]
{:step (fn [v]
(if (< (swap! *n* dec) 1)
[v reduced]
[v]))})))))
(defn demo [msg xf] [msg (into [] xf (range 10))])
(prn [(demo "Keep odd numbers" (ezfilter odd?))
(demo "take first 3" (eztake 3))
(demo "Composing previous transducers" (comp (ezfilter odd?) (eztake 3)))])
;;
;; 2nd take
;;
(defn steps [reducer a vs]
(if (or (reduced? a) (empty? vs))
a
(let [[v & vs] vs]
(if (= v reduced)
(reduced a)
(recur reducer
(reducer a v) ;; step
vs)))))
(defn steps-reducer [reducer f]
(fn
([] (reducer)) ;; init
([a v] (steps reducer a (f v))) ;; step
([a] (reducer (unreduced (steps reducer a (f))))))) ;; result
(defn ezducer2
([f] ;; stateless
(fn [reducer]
(steps-reducer reducer f)))
([f state] ;; stateful
(fn [reducer]
(let [*state* (atom state)
stateful-f (fn
([] (let [[_ vs] (f @*state*)] vs))
([v] (let [[state vs] (f @*state* v)]
(reset! *state* state)
vs)))]
(steps-reducer reducer stateful-f)))))
(defn ezfilter2 [pred]
(ezducer2
(fn
([] [])
([v] (if (pred v) [v] [])))))
(def drop-all2
(ezducer2
(fn
([] [])
([_] []))))
(defn eztake2 [n]
(if (< n 1)
drop-all2
(ezducer2
(fn
([n] [n []])
([n v]
(let [n (dec n)]
[n (if (< n 1) [v reduced] [v])])))
n)))
(defn demo [msg xf] [msg (into [] xf (range 10))])
(prn [(demo "[2nd] Keep odd numbers" (ezfilter2 odd?))
(demo "[2nd] take first 3" (eztake2 3))
(demo "[2nd] Composing previous transducers" (comp (ezfilter2 odd?) (eztake2 3)))])
;;
;; 3rd take
;;
(defn ezducer3 [& {:keys [step result state]}]
(let [[step result]
(if (nil? state)
(let [step' (if (nil? step) (fn [v] [v]) step)
result' (if (nil? result) (fn [] []) result)]
[step' result'])
(let [*state* (atom state)
step' (if (nil? step) (fn [s v] [s [v]]) step)
result' (if (nil? result) (fn [s] [s []]) result)
step'' (fn [v]
(let [[state vs] (step' @*state* v)]
(reset! *state* state)
vs))
result'' (fn []
(let [[_state vs] (result' @*state*)]
vs))]
[step'' result'']))]
(fn [reducer]
(letfn [(steps [a [v :as vs]]
(if (or (reduced? a) (empty? vs))
a
(if (= v reduced)
(reduced a)
(recur (reducer a v) ;; step
(rest vs)))))]
(fn
([] (reducer)) ;; init
([a v] (steps a (step v)))
([a] (reducer (unreduced (steps a (result)))))))))) ;; result
(defn ezfilter3 [pred]
(ezducer3
:step (fn [v] (if (pred v) [v] []))))
(def drop-all3
(ezducer3
:step (fn [_] [])))
(defn eztake3 [n]
(if (< n 1)
drop-all3
(ezducer3
:state n
:step (fn [n v]
(let [n (dec n)]
[n (if (< n 1)
[v reduced]
[v])])))))
(prn [(demo "[3rd] Keep odd numbers" (ezfilter3 odd?))
(demo "[3rd] take first 3" (eztake3 3))
(demo "[3rd] Composing previous transducers" (comp (ezfilter3 odd?) (eztake2 3)))])
;;
;; 4th take
;;
(defn ezducer4
([constructor]
(fn [reducer]
(let [*a* (atom)
step! (fn
([] (reduced? (swap! *a* (fn [a] (if (reduced? a) a (reduced a))))))
([v] (reduced? (swap! *a* (fn [a v] (if (reduced? a) a (reducer a v))) v))))
{:keys [step result]} (constructor step!)
step (if (nil? step) (fn [v] (step! v)) step)
result (if (nil? result) (fn []) result)]
(fn
([] (reducer)) ;; init
([a v] (reset! *a* a) (step v) @*a*)
([a] (reset! *a* a) (result) (reducer (unreduced @*a*))))))) ;; result
([state constructor]
(fn [reducer]
(let [*a* (atom)
step! (fn
([] (reduced? (swap! *a* (fn [a] (if (reduced? a) a (reduced a))))))
([v] (reduced? (swap! *a* (fn [a v] (if (reduced? a) a (reducer a v))) v))))
{:keys [step result]} (constructor step!)
step (if (nil? step) (fn [state v] (step! v) state) step)
result (if (nil? result) (fn [_]) result)
*state* (atom state)]
(fn
([] (reducer)) ;; init
([a v] (reset! *a* a) (swap! *state* step v) @*a*)
([a] (reset! *a* a) (swap! *state* result) (reducer (unreduced @*a*)))))))) ;; result
(def drop-all4
(ezducer4
(fn [step]
{:step (fn [_])})))
(defn eztake4 [n]
(if (< n 1)
drop-all4
(ezducer4
n ;; state initialized
(fn [step]
{:step (fn [n v]
(step v)
(let [n (dec n)]
(when (< n 1)
(step))
n))}))))
(prn [(demo "[4th] Keep odd numbers" (ezfilter4 odd?))
(demo "[4th] take first 3" (eztake4 3))
(demo "[4th] Composing previous transducers" (comp (ezfilter3 odd?) (eztake4 4)))])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment