liveBook · Manning

Reduction operation

As explained in Clojure: The Essential Reference

reduce is a Clojure function that combines collection elements by repeatedly applying a reducing function to an accumulator and the next element.

A reduction turns a collection into a result by repeatedly combining an accumulator with the next element. In Clojure, `(reduce f coll)` uses the first element as the initial accumulator; `(reduce f val coll)` starts with `val`. The reducing function receives the result so far and the next element, then returns the new result. That result need not be a number: it can be a map or another collection. section 3.5.4

The starting value matters most at the edges:

  • With an empty collection and no initial value, `reduce` calls `f` with no arguments. Thus `(reduce + nil)` returns `0`, while `(reduce / [])` throws an `ArityException`. section 3.5.4
  • With an explicit initial value, that value is the accumulator before the first element is processed. section 3.5.4

Finish a processing pipeline

`(->> (range 5) (map #(* % %)) (reduce +))` returns `30`: `map` produces the squares `0, 1, 4, 9, 16`, and `reduce` sums them. `(reductions + (map #(* % %) (range 5)))` instead returns the intermediate totals `(0 1 5 14 30)`, making the accumulation visible. section 3.5.4

(defn count-occurrences [coll]
  (->> coll
       (map #(vector % 1))                       ; #1
       (reduce (fn [m [k cnt]]                   ; #2
         (assoc m k (+ cnt (get m k 0)))) {})))  ; #3

(defn word-count [s]
  (count-occurrences (.split #"\s+" s)))

(word-count "To all things, all men, all of the women and children")

;;{"To" 1
;; "all" 3
;; "and" 1
;; "children" 1
;; "men," 1
;; "of" 1
;; "the" 1
;; "things," 1
;; "women" 1}

A word-count implementation uses `{}` as its initial accumulator. For each `[word 1]` pair, the reducing function updates the word’s count in the map. This illustrates why a reduction’s result need not be a scalar; for this particular job, `frequencies` is a shorter alternative. section 3.5.4

`reduce` produces the final result; `reductions` exposes successive accumulator values. A reducing function can also return a `reduced` value to stop processing early. Ordinary reduction consumes its input eagerly, so consider the size of an accumulator that grows on every step. section 3.5.4 section 7.0

The same reducing-function contract supports collection-processing alternatives. Reducers defer composed transformations until reduction runs, while `fold` can split supported collections into chunks and combine their results in parallel. Parallel combination requires an associative combining function; it is not automatically faster for small inputs. section 7.1 section 7.1.1

A reduction is a useful final step when each input can update a compact result. It is not invariably the fastest way to combine values: for string concatenation, `(apply str values)` can outperform `(reduce str values)` because repeated `str` calls discard intermediate builders. section 3.5.4 section 19.1

How Clojure: The Essential Reference covers it

How deep it goes
Explains arities, accumulator updates, empty-input behavior, intermediate results, early termination, and memory pitfalls, then relates the contract to parallel folding.
Examples
Works through sums of squares, intermediate totals, word counts, running averages, and string concatenation benchmarks.
What it assumes
Assumes familiarity with Clojure functions and collections while explaining the accumulator and reducing-function vocabulary.
What you can do after
Build it from scratch with a two-argument reducing function and an appropriate initial accumulator; choose reductions when intermediate results are needed.
How it connects
Connects reduce to map, filter, reductions, reducers, transducers, and fold.

Also explained in

Compare how 9 books explain reduction operation →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Clojure: The Essential Reference. The section links point to the parts of the book it draws on.