LISP - Using reduce on List
Lisp - Folding Lists with reduce
Section titled “Lisp - Folding Lists with reduce”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.
Syntax
Section titled “Syntax”(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 reductionfunction.
Example 1: Summing Numbers
Section titled “Example 1: Summing Numbers”The most classic use of reduce is to sum a list of numbers.
main.lisp
Section titled “main.lisp”(print (reduce #'+ '(1 2 3 4)))
; How it works:; 1. (+ 1 2) -> 3; 2. (+ 3 3) -> 6; 3. (+ 6 4) -> 10Output
Section titled “Output”10Example 2: Finding the Maximum Number
Section titled “Example 2: Finding the Maximum Number”You can use any two-argument function, like max.
main.lisp
Section titled “main.lisp”(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) -> 41Output
Section titled “Output”41Advanced Usage
Section titled “Advanced Usage”Using an :initial-value
Section titled “Using an :initial-value”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))Output
Section titled “Output”1100Reducing from the Right with :from-end
Section titled “Reducing from the Right with :from-end”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))Output
Section titled “Output”37Using a lambda for Custom Logic
Section titled “Using a lambda for Custom Logic”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)))Output
Section titled “Output”8Common Pitfalls and Best Practices
Section titled “Common Pitfalls and Best Practices”reduceon an Empty List: Callingreduceon an empty sequence without an:initial-valuewill signal an error. Always provide an:initial-valueif the sequence might be empty.reducevs.loop:reduceis excellent for clean, functional-style accumulation into a single value. For more complex iterations involving side effects, multiple accumulators, or complex termination conditions, theloopmacro is often more readable and powerful.