Skip to content

LISP - Using reduce on List

reduce is a powerful higher-order function in Common Lisp for processing sequences. It ‘reduces’ a sequence (like a list or a vector) to a single value by repeatedly applying a given function to its elements. This pattern is also known as ‘fold’ or ‘accumulate’ in other programming languages.

(reduce function sequence &key from-end start end initial-value key)

Where:

  • function: A function (or a symbol naming a function) that accepts two arguments: an accumulated result and the next element.
  • sequence: The list, vector, or other sequence to be processed.
  • :initial-value (optional): The starting value for the reduction. If not provided, the first element(s) of the sequence are used to start the process.
  • :from-end (optional): If true (t), the reduction starts from the rightmost elements instead of the left.
  • :key (optional): A function to be applied to each element before it is passed to the reduction function.

The most classic use of reduce is to sum a list of numbers.

(print (reduce #'+ '(1 2 3 4)))
; How it works:
; 1. (+ 1 2) -> 3
; 2. (+ 3 3) -> 6
; 3. (+ 6 4) -> 10
10

You can use any two-argument function, like max.

(print (reduce #'max '(1 12 3 41 25)))
; How it works:
; 1. (max 1 12) -> 12
; 2. (max 12 3) -> 12
; 3. (max 12 41) -> 41
; 4. (max 41 25) -> 41
41

The :initial-value provides a starting point for the accumulation. This is crucial when reducing an empty list, which would otherwise cause an error.

; Summation starts from 100 instead of the first element.
(print (reduce #'+ '(1 2 3 4) :initial-value 100))
; Returns the initial value if the list is empty.
(print (reduce #'+ '() :initial-value 0))
110
0

For non-associative functions like subtraction, the order matters. :from-end reverses the order of operations.

; Left-to-right (default): ((10 - 5) - 2) -> 3
(print (reduce #'- '(10 5 2)))
; Right-to-left: (10 - (5 - 2)) -> 7
(print (reduce #'- '(10 5 2) :from-end t))
3
7

You can provide a custom anonymous function (lambda) for more complex reductions, like finding the length of the longest string in a list.

(let ((words '("lisp" "is" "powerful")))
(print
(reduce (lambda (longest-len current-word)
(max longest-len (length current-word)))
words
:initial-value 0)))
8
  • reduce on an Empty List: Calling reduce on an empty sequence without an :initial-value will signal an error. Always provide an :initial-value if the sequence might be empty.
  • reduce vs. loop: reduce is excellent for clean, functional-style accumulation into a single value. For more complex iterations involving side effects, multiple accumulators, or complex termination conditions, the loop macro is often more readable and powerful.