In the previous lecture, we introduced SML's predefined datatype
int list for lists of integers.
An important point is that the list datatype is defined
recursively:
An int list is one
of the following (and nothing else):
Last time and today we wrote some functions taking list arguments and/or returning list values, and we showed how to prove theorems about lists using structural induction.
We discussed tail recursion, a form of recursion that is somewhat like the use of loops in imperative programming. This form of recursion is often used to make functions more efficient.
Terminology: A function is tail recursive if it is recursive and if it performs no computations after any recursive call that it makes. This means in particular that the function directly returns any value obtained from any recursive call that it makes. More generally, a function f is said to make a tail call to function g if calling g is the final computation f performs. From that perspective, a recursive function f is tail recursive if any calls to itself are tail calls.
(Clarifying Comment: If the body of a function contains multiple locations at which a recursive call occurs, then every one of those recursive calls must be a tail call in order for the function to be considered tail recursive.)
Accumulator arguments play an important role in tail recursion. The presence of an accumulator argument in a function forces us to think carefully about the specification of the function and to prove corresponding theorems of correctness.