Skip to content

Instantly share code, notes, and snippets.

@taylorwood
Created September 23, 2018 23:44
Show Gist options
  • Save taylorwood/47cfb3ec0cfb92dfc25b834f5db0320f to your computer and use it in GitHub Desktop.
Save taylorwood/47cfb3ec0cfb92dfc25b834f5db0320f to your computer and use it in GitHub Desktop.
Clojure transducers
(defn zip
"Returns a transducer that zips cs with inputs."
[& cs]
(fn [rf]
(let [cs (volatile! cs)]
(fn
([] (rf))
([result] (rf result))
([result item]
(if (every? seq @cs)
(let [firsts (map first @cs)]
(do (vswap! cs #(map rest %))
(rf result (cons item firsts))))
result))))))
(comment
(sequence
(zip '[a b c] '[x y z])
(range 10))
=> ((0 a x) (1 b y) (2 c z)))
(defn zip-with
"Like zip but applies input with cs values to f."
[f & cs]
(fn [rf]
(let [cs (volatile! cs)]
(fn
([] (rf))
([result] (rf result))
([result item]
(if (every? seq @cs)
(let [firsts (map first @cs)]
(do (vswap! cs #(map rest %))
(rf result (apply f item firsts))))
result))))))
(comment
(sequence
(zip-with str '[a b c] '[x y z])
(range 10))
=> ("0ax" "1by" "2cz"))
(defn itermap
"Takes a function with 1-arity for first input and 2-arity for each
additional input, where the first argument is the result of the previous
invocation. Returns a map-like transducer with iterate-like behavior."
[f]
(fn [rf]
(let [prev (volatile! ::void)]
(fn
([] (rf))
([result] (rf result))
([result input]
(let [p @prev
value (if (= ::void p)
(f input)
(f p input))]
(vreset! prev value)
(rf result value)))
([result input & inputs]
(let [p @prev
value (if (= ::void p)
(apply f input inputs)
(apply f p input inputs))]
(vreset! prev value)
(rf result value)))))))
(comment
(sequence (itermap +) (range 10))
=> (0 1 3 6 10 15 21 28 36 45)
(sequence (itermap str) (range 5))
=> ("0" "01" "012" "0123" "01234")
(def vowels #{\a \e \i \o \u \y})
(def alphabet (into #{} (map char) (range 97 123)))
(sequence
(comp
(itermap (fn
([v a] a)
([prev v a]
(if (and (vowels prev)
(< 3/4 (rand)))
a
v))))
(partition-all 5)
(map #(apply str %))
(take 5))
(repeatedly #(rand-nth (seq vowels)))
(repeatedly #(rand-nth (seq alphabet))))
=> ("unagi" "oyuuu" "aouae" "yoaiu" "ouuwe"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment