Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save halcyon/24d2be2b440f96972f886d32368047ea to your computer and use it in GitHub Desktop.
Save halcyon/24d2be2b440f96972f886d32368047ea to your computer and use it in GitHub Desktop.
Clojure's Transducers and Reducers

Transducers and Reducers

Notes and examples of Transducers and Reducers

Reducers

  • Used on collections

  • Eager evaluation only. (not lazy)

  • The operation(s) and the input are no longer tied together

     
(r/map zero?)

;=> #<reducers$map$fn__2527 clojure.core.reducers$map$fn__2527@7201f000>

(let [map-zero? (r/map zero?)
      inputs [0 1 0 1 0 1]]
  (reduce conj [] (map-zero? inputs)))

;=> [true false true false true false]
  • Can be combined into a single function for better performance. (composable)
(def zero?-and-not-false 
  (comp 
   (r/filter true?)
   (r/map zero?)))     

;=> #'user/zero?-and-not-false

(reduce conj [] (zero?-and-not-false [0 1 0 1 0 1]))

[true true true]

Transducers

  • Can be used in ClojureScript

  • Function C is the reducing function

  • Function B calls Function C

  • Function A creates Function B

  • (A C) -> B

(defn a [c]  
  (fn b    
    ([] (c))    
    ([coll] (c coll))    
    ([coll input] (c coll input))))
	  
;=> #'user/a

(transduce a + [1 2 3])

;=> 6    
  • Doesn't care about the input type

  • Transducers seem to be faster than reducers

     
     (dotimes [n 5] (time (r/reduce + 0 [1 2 3])))
     
     "Elapsed time: 0.23142 msecs"
     "Elapsed time: 0.047252 msecs"
     "Elapsed time: 0.043944 msecs"
     "Elapsed time: 0.062372 msecs"
     "Elapsed time: 0.05938 msecs"

     ;=> nil
     
     (dotimes [n 5] (time (transduce a + 0 [1 2 3])))
     
     "Elapsed time: 0.1257 msecs"
     "Elapsed time: 0.026548 msecs"
     "Elapsed time: 0.018166 msecs"
     "Elapsed time: 0.031276 msecs"
     "Elapsed time: 0.024773 msecs"
     
     ;=> nil
  • Collection fns of previous Clojure versions are now optionally Transducers
(let [*2 #(* % 2)
      *4 #(* % 4)]
  
  (def weird-composition       
    (comp 
     (filter even?)
     (map *2)
     (map *4))))

;=> #'user/weird-composition

(into [] weird-composition [1 2 3 4])

;=> [16 32]
  • For lazy transformations you must use sequence
(def star-wrap
  (map #(str "*" % "*")))

;=> #'user/star-wrap

(into [] star-wrap [1 2 3])

;=> ["*1*" "*2*" "*3*"]

(sequence star-wrap [1 2 3])

;=> ("*1*" "*2*" "*3*")

(type (into [] star-wrap [1 2 3]))

;=> clojure.lang.PersistentVector

(type (sequence star-wrap [1 2 3]))

;=> clojure.lang.LazyTransformer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment