Reduce In Clojure

The last of the Big Three Higher Order Functions in Clojure is reduce. In other languages it is called fold, accumulate, aggregate, compress or inject.

Sometimes reduce is compared to aggregate functions in databases, like average, count, sum, min or max. These functions take multiple values, and produce one value. Some functions in Clojure, like the four main math functions (+, -, * and /) are processed through reduce if they are called with more than two values.

I would like to point out that unlike the other aggregation functions that databases have, “average” cannot be done with reduce. To do an average, you sum the amounts, and then divide by the number of elements. No implementation of reduce (or fold or whatever you call it) keeps track of the number of elements and allows for an additional operation at the end. Maybe some languages or implementations have a reduce and an enhanced-reduce to cover this case.

The first parameter to Clojure’s reduce is a function that takes two parameters. There is an optional parameter that is an initial value, and the last argument is the collection you want to process. Reduce takes either the option initial value and the first element of the collection, or the first two elements of the collection, and passes them to the function. The output of the function is then used along with the next element in the collection. Reduce keeps using the output from the previous element and the next element as arguments to the function until the collection is exhausted.

I cannot find it now, but I remember reading somewhere that reduce can be hard to understand because “reduce” is frankly not a good name for it, and there are many functions that do what reduce does. As stated, some of the functions in Clojure (like max, min, and some math functions) call reduce. So you are actually using reduce a lot under the covers.

There is a cartoon about this. A teacher gives a student a few chances to say what is “2 + 3” equal to. The student says it equal to “2 + 3” and it is also equal to “3 + 2”. The student gets an F. The cartoon ends with: “Sad fact: many math teachers do not know the difference between equality and reduction.” I admit I do not either. A lot of the cartoons are about Haskell, which I have no desire to learn.

;; call + on a bunch of numbers
user> (+ 1 2 3 4 5 6)
21
;; winning with "the duce"
user> (reduce + [1 2 3 4 5 6])
21
;; using 0 for initial value
user> (reduce + 0 [1 2 3 4 5 6])
21
;; use 0 for +, 1 for *, or you will get incorrect results
user> (reduce + 1 [1 2 3 4 5 6])
22
;; define a function to explain reduce w/print statements
user> (defn add-with-print [x y]          
  (println "x is " x ", y is " y  ", (+ x y) is " (+ x y))
  (+ x y))
#'user/add-with-print
;; use w/just collection
user> (reduce add-with-print [1 2 3 4 5 6])
x is  1 , y is  2 , (+ x y) is  3           
x is  3 , y is  3 , (+ x y) is  6           
x is  6 , y is  4 , (+ x y) is  10          
x is  10 , y is  5 , (+ x y) is  15         
x is  15 , y is  6 , (+ x y) is  21         
21
;; use w/0 as initial value
user> (reduce add-with-print 0 [1 2 3 4 5 6])
x is  0 , y is  1 , (+ x y) is  1           
x is  1 , y is  2 , (+ x y) is  3           
x is  3 , y is  3 , (+ x y) is  6           
x is  6 , y is  4 , (+ x y) is  10          
x is  10 , y is  5 , (+ x y) is  15         
x is  15 , y is  6 , (+ x y) is  21         
21
;; use with 1 as initial value
user> (reduce add-with-print 1 [1 2 3 4 5 6])
x is  1 , y is  1 , (+ x y) is  2           
x is  2 , y is  2 , (+ x y) is  4           
x is  4 , y is  3 , (+ x y) is  7           
x is  7 , y is  4 , (+ x y) is  11          
x is  11 , y is  5 , (+ x y) is  16         
x is  16 , y is  6 , (+ x y) is  22         
22
user>

You’re welcome.

Update 2022-02-10_22:33:14: I just read a post by Rich Hickey about transducers in which he talks a bit about reduce. The function you send to reduce takes two arguments: the result-so-far and the next item in the collection, and returns the next result-so-far.

Image from  Aurora Consurgens, a 15th century manuscript housed at Central Library of Zurich. Image from e-Codices. This image is assumed to be allowed under Fair Use.