Recursion
Functions that call themselves
A function is recursive when it calls itself (Def 1.17). Recursion is how SML expresses loops and iteration.
Every well-formed recursive function has two parts:
1. Base case(s) — handle the smallest input directly, without recursion.
2. Recursive case(s) — handle larger inputs by calling the function on a smaller sub-piece.
Example — length of a list:
fun length nil = 0
| length (_ :: t) = 1 + length t;Base case: empty list has length 0. Recursive case: a non-empty list has length 1 plus the length of its tail.
Example — sum of a list:
fun sum nil = 0
| sum (h :: t) = h + sum t;Example — factorial (n!):
fun fact 0 = 1
| fact n = n * fact (n - 1);Tail calls (Def 1.37, slides p.227): when the recursive call is the LAST thing the function does, modern compilers optimize it into a loop. So tail-recursive functions are as efficient as for-loops. Compare:fun sum (h :: t) = h + sum t; — NOT tail-recursive (the + happens after the recursive call returns).fun sum_tr nil acc = acc | sum_tr (h :: t) acc = sum_tr t (acc + h); — tail-recursive with an accumulator.
- 0.0sRecursion in SML: length [1, 2, 3]
- 1.4sRoot call: length [1, 2, 3]
- 2.8sUnfolds to 1 + length [2, 3]
- 3.9sThen 1 + 1 + length [3]
- 5.0sThen 1 + 1 + 1 + length []
- 6.1sBase case length [] highlighted in amber
- 7.3sTree fades, final result 1 + 1 + 1 + 0 = 3
- 9.3sEach recursive call passes a shorter list
- 10.5sTermination is guaranteed by the shrinking input
Theorem. is bijective if and only if it has an inverse with and for all .
Proof. () Assume is bijective.
- Injectivity: for each , there is at most one with .
- Surjectivity: for each , there is at least one with .
- Combined: there is exactly one per . Define to be that .
- Then (by definition) and (also by definition). ✓
() Assume exists with both composition identities.
- Injectivity: if , then . ✓
- Surjectivity: for any , let . Then . ✓
So is bijective.
The fold (or reduce) operator collapses a list into a single value by repeatedly combining adjacent elements. For binary operation with identity :
-
Examples:
- (sum).
- (product).
- (AND).
- (OR).
In SML this is foldl and foldr (with the difference being association order).
fun fact 0 = 1 | fact n = n * fact (n-1) compute?fun sum (h :: t) = h + sum t tail-recursive?