Last active
August 27, 2024 19:29
-
-
Save xfthhxk/99a38340c5d0e36331a2b29ffcc93157 to your computer and use it in GitHub Desktop.
Clojure Transducer Notes
This file contains 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
;; Transducer a portmanteau of transform and reducer? | |
(defn reducing-fn | |
([] | |
;; this 0 arity is called when no initial coll is provided to a transducer | |
;; This should return the starting state | |
(transient #{})) | |
([ans] | |
;; this arity is called with the accumulator/answer after all items in the | |
;; collection have been iterated over. This is the completion step of the | |
;; transducing process. The clojure.core/completing fn facilitates this | |
;; step for a fn that only has 0 and 2 arities. | |
(into (sorted-set) (persistent! ans))) | |
([ans x] | |
;; this arity is called with the accumulator and each item in the input collection | |
;; after it has been through the xform | |
(conj! ans x))) | |
(transduce (comp (remove even?) | |
(map #(* % %))) | |
reducing-fn | |
(range 10)) ;; => #{1 9 25 49 81} | |
---- | |
;; eduction vs seqs | |
(defn my-inc | |
[x] | |
(println "my-inc: " x) | |
(inc x)) | |
(def inc-res (->> (range 5) | |
(filter identity) | |
(map my-inc))) | |
(reduce + inc-res) ;; => 15 & side effects include printlns | |
;; my-inc: 0 | |
;; my-inc: 1 | |
;; my-inc: 2 | |
;; my-inc: 3 | |
;; my-inc: 4 | |
;; no matter how many times you evaluate (reduce + inc-res), the printlns will print only once | |
;; This is because inc-res seq is realized at some point and does not need to be evaluated every time it is used | |
;; Of course, for a large seq you could run out of memory | |
;;----------------------------------------- | |
;; Eduction | |
;;----------------------------------------- | |
(def inc-educ | |
(eduction (filter identity) (map my-inc) (range 5))) | |
(reduce + inc-educ) ;; => 15 and also side effects each time this form is evaluated | |
;; Eduction does not cache the resulting seq and hence is memory efficient. | |
;; As a result, each time `(reduce + inc-educ)` is called the side effects will happen | |
;; ie evaluate `(reduce + inc-educ)` twice you'll see | |
;; my-inc: 0 | |
;; my-inc: 1 | |
;; my-inc: 2 | |
;; my-inc: 3 | |
;; my-inc: 4 | |
;; my-inc: 0 | |
;; my-inc: 1 | |
;; my-inc: 2 | |
;; my-inc: 3 | |
;; my-inc: 4 | |
;;------------------------ | |
;; completing | |
;;------------------------ | |
;; The completing function is needed only when you have a 2 arity fn | |
;; or want to modify the behavior of an existing fn to handle the | |
;; 1 arity case specially. | |
(fn | |
([] ,,,) | |
([ans x] ,,,)) | |
;; and you need to simply add the 1 arity version | |
(fn [ans] ,,,) | |
(defn not-empty-kws | |
[xs] | |
(transduce (comp (map str/lower-case) | |
(map keyword)) | |
(completing conj not-empty) | |
#{} | |
xs)) | |
(not-empty-kws ["a" "b"]) ;; => #{:b :a} | |
;; instead of #{} you get nil in this case | |
(not-empty-kws []) ;; => nil | |
;;-------------------------------------------------------------------- | |
;; Samples | |
;;-------------------------------------------------------------------- | |
(->> [{:ids #{1 2}} {:ids #{3}}] | |
(mapcat :ids) | |
set) | |
(transduce (map :ids) | |
into | |
#{} | |
[{:ids #{1 2}} {:ids #{3}}]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment