mapcar and Functional Iteration
Lisp encourages a functional style of iteration where, instead of writing an explicit loop with a counter and manual index bookkeeping, you describe what transformation you want applied to each element and let a mapping function handle the traversal. mapcar is the most commonly used of these: (mapcar function list) applies function to each element in turn and collects the results into a new list, leaving the original list untouched.
Cricket analogy: Instead of a scorer manually recalculating strike rate for each batsman one by one with a calculator, a stats app applies the 'strike rate' formula uniformly across every batsman's row — exactly like mapcar applying a function across every list element automatically.
Basic mapcar: Transforming One or More Lists
(mapcar #'1+ '(1 2 3)) returns (2 3 4) — the sharp-quote before 1+ passes the function itself as a value, since function names aren't automatically treated as values the way variable names are. mapcar can also take multiple lists of matching length: (mapcar #'+ '(1 2 3) '(10 20 30)) walks both lists in parallel, calling (+ 1 10), (+ 2 20), (+ 3 30), and returns (11 22 33). If the lists are of unequal length, mapcar stops as soon as the shortest one runs out.
Cricket analogy: Pairing each batsman's runs list with a matching balls-faced list and computing strike rate for each pair in parallel — (runs[i] / balls[i]) — mirrors mapcar walking two lists together element by element.
;; single list
(mapcar #'1+ '(1 2 3)) ; => (2 3 4)
;; multiple lists walked in parallel
(mapcar #'+ '(1 2 3) '(10 20 30)) ; => (11 22 33)
;; inline lambda instead of a named function
(mapcar (lambda (x) (* x x)) '(1 2 3 4)) ; => (1 4 9 16)The Wider Mapping Family: mapc, maplist, mapcan
mapcar isn't alone: mapc behaves identically but discards the return values and returns the original first list instead, making it the idiomatic choice when you only care about a function's side effects, like printing. maplist is subtly different — instead of passing each element to the function, it passes each successive cdr (i.e., each sublist, or 'tail'), which is useful when a computation needs to see 'the rest of the list from here' rather than just the current item. mapcan is like mapcar but expects the function to return lists, which it then splices together destructively with nconc rather than collecting into a nested list.
Cricket analogy: mapc is like a commentator calling out each ball's result purely for the crowd's benefit, discarding the description afterward rather than compiling it into a report — used purely for the side effect, not the return value.
Lambda Expressions as Inline Functions
You don't need a separately named function for every transformation — (lambda (x) (* x x)) creates an anonymous function on the spot, and #'(lambda (x) (* x x)) or the shorthand (lambda (x) ...) can be passed directly to mapcar wherever a function is expected. This is idiomatic for small, one-off transformations, while defun-defined named functions are preferred when the logic is reused across several call sites or is complex enough to benefit from a descriptive name.
Cricket analogy: A one-off, ad hoc 'net run rate for just this match' calculation scribbled on a notepad, used once and discarded, mirrors a lambda's throwaway, unnamed nature versus a permanently defined stat formula.
#'foo is shorthand for (function foo), which retrieves the function value bound to the symbol foo (its function cell) rather than its variable value — this is Common Lisp's Lisp-2 nature, where functions and variables live in separate namespaces. 'foo, by contrast, would pass the bare symbol as data, not as something callable, so mapcar given 'foo instead of #'foo would signal an error.
Folding with reduce
Where mapcar transforms each element independently, (reduce function list) combines elements together into a single accumulated result by repeatedly applying function to a running value and the next element — (reduce #'+ '(1 2 3 4)) computes (((1+2)+3)+4) and returns 10. reduce accepts an :initial-value keyword for seeding the accumulator (essential for reducing an empty list safely) and a :from-end keyword to fold right-to-left instead of left-to-right, which matters for non-associative operations.
Cricket analogy: Reducing a list of individual over scores down to a single match total by repeatedly adding each over's runs to a running sum mirrors reduce folding a list into one accumulated value with #'+.
(reduce #'+ '(1 2 3 4)) ; => 10
(reduce #'+ '() :initial-value 0) ; => 0, safe on empty lists
(reduce (lambda (acc x) (cons x acc)) '(1 2 3) :initial-value '())
;; => (3 2 1), a hand-rolled reverse via reduce- mapcar applies a function across one or more lists in parallel, collecting results into a new list.
- #'function-name (or (function function-name)) is required to pass a function as a value, distinct from quoting its name.
- mapc discards return values and is used for side effects; maplist passes successive sublists rather than elements.
- mapcan splices the function's list results together destructively, unlike mapcar's plain collection.
- Lambda expressions create anonymous, inline functions ideal for small one-off transformations.
- reduce folds a list down to a single accumulated value, with :initial-value guarding against empty-list errors.
- Functional iteration style avoids manual index/counter bookkeeping that explicit loops require.
Practice what you learned
1. What does (mapcar #'1+ '(1 2 3)) return?
2. Why is #' required before a function name passed to mapcar, e.g. #'1+?
3. How does mapc differ from mapcar?
4. What does (reduce #'+ '(1 2 3 4)) compute?
5. What is the key difference between mapcar and mapcan when the function returns lists?
Was this page helpful?
You May Also Like
Lists and Cons Cells
How Lisp builds every list out of two-slot cons cells, and how car, cdr, and quoting let you construct and inspect them.
List Manipulation Functions
The core built-in functions for navigating, searching, combining, and transforming lists, and the crucial distinction between destructive and non-destructive operations.
Vectors and Hash Tables in LISP
When to reach for vectors and hash tables instead of lists — constant-time indexed and keyed access versus linked-chain traversal.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics