the Philippine Islands [optional note] |
Recursion is a technique for writing programs. Even when we think we know a word, checking out a dictionary definition can help us to understand it better. You can check out the definition of recursion at Merriam-Webster Online, which dates the first known use of "recursion" to 1790. Here is that definition, edited lightly for our purposes:
re · cur · sion
- RETURN sense 1: to go back or come back again
- the determination of a succession of elements (such as numbers or functions) by operation on one or more preceding elements according to a rule or formula involving a finite number of steps
- a computer programming technique involving the use of ... a function or algorithm that calls itself ...
To recur is to go back, in thought or discourse. A function can do that.
In computer science, a recursive program is one that:
As a result of the second part of this definition, we can see that a recursive program is defined, in part, in terms of itself. In practice, we create a function that calls itself.
We sometimes use recursive relationships to understand mathematical properties. For example, we can examine a sequence of values containing a number raised to a power and see a pattern:
power(x, 0) = 1 power(x, 1) = 1 * x = x * power(x, 0) power(x, 2) = 1 * x * x = x * power(x, 1) power(x, 3) = 1 * x * x * x = x * power(x, 2) ...
We can turn this into something more usable by turning the power itself into a variable and using an inductive definition to make the pattern explicit:
power(x, 0) = 1 power(x, n) = x * power(x, n-1)
In fact, there are programming languages -- such as Prolog and Haskell -- in which you write the recursive equations just like that!
In Racket, we would write:
(define power ; behaves like the built-in function expt (lambda (x n) (if (zero? n) 1 (* x (power n (sub1 n))))))
The fundamental idea behind recursion is this: If a problem can be defined in terms of a similar, yet simpler problem, recursion may be a useful tool for expressing a solution.
"O, thou hast damnable iteration and
are indeed able to corrupt a saint."
-- Falstaff,
in Shakespeare's Henry IV
Most programmers learn to write loops first and then see recursion as a more difficult way to do things they can already do. But I think you'll find iteration and recursion are really pretty similar. More importantly, you think about the same things no matter which one you are using.
Consider the task described in the song "99 Bottles of Beer on the Wall".
How might we solve this task with a loop?
n = 99; while (n > 0) { take_one_down(); pass_it_around(); n--; } sing_last_verse();
Notice that you get to solve "n-1 bottles" with the same code that solved "n bottles". That's exactly what we do when we write a recursive solution:
bottles_of_beer(99)where
bottles_of_beer(0) = sing_last_verse() bottles_of_beer(n) = take_one_down() pass_it_around() bottles_of_beer(n-1)
In both cases, you have to answer the same questions:
Iteration and recursion really are the same kind of process. You may even have noticed that the dictionary definition of "recursion" we saw earlier says as much. The third entry of that definition ends with the phrase compare iteration!
Be not afraid. If you can write a loop, then you, too, can write recursive functions.
(See this footnote for the source of this example and for a little unexpected goodness.)
More formally, we will say that every recursive program consists of:
The base cases are often pretty straightforward, but we still have to think. They encode our understanding of the problem by recording the answer to the smallest problem(s) in the domain.
Each recursive case consists of three steps:
This is usually where the descriptions of recursion end in our textbooks. "Okay," you might say, "great. But how do I do that?" The goal of the next few weeks is to help you feel this TL;DR in your bones:
Recursion doesn't have to be scary. Sometimes, it's all about the data.
In particular, there are rules that help us to think about each of these steps:
The driver for this process is the the type of the arguments that our function processes. This is where inductive specifications can help.
I adopted my "99 Bottles of Beer on the Wall" example from this message on an Erlang mailing list. (Erlang is a cool programming language: not quite functional, not quite imperative. It handles concurrency as a primitive!)
That message continues with a bit of news that surprises many programmers who grew up with iteration before learning recursion: Iteration is a special case of recursion -- with limits.
Take a look at our examples above. In the loop, we always handle the one bottle first, then the remaining n-1 bottles. But in the recursive function, we could make the recursive call first to handle the n-1 bottles first, and then handle the one bottle last. We could even make the call between "take one down" and "pass it around", and interleave the activities! Try that with a loop.
In both iteration and recursion, a program handles an arbitrary number of steps with a single piece of code. This code either branches to the top of a loop (an implicit jump) or makes a function call (an explicit jump) -- and re-enters itself, so that one piece of code handles any number of things. In both cases, you have to make sure that some measure of the problem size (the length of a list, the number of things left to work on, the size of a tree, etc.) is strictly smaller every time you enter it.
But the function call is more powerful. As the creator of Scheme wrote back in the 1970s, lambda is the ultimate goto.
Take a close look at this image. The big island is Luzon, one of the Philippine Islands. In the middle of Luzon is Lake Taal. Inside Lake Taal is Vulcano Island. Notice Crater Lake, the dot of water in the middle of Vulcano Island. Crater Lake holds a unique distinction. It is the largest lake on an island in a lake on an island in the world.
How's that for recursion?
This is a bit more complicated than the simplest recursion. We have a lake on an island in a lake on an island. Two different kinds of objects are recursing back and forth between one another. As we learn next session, this is a special kind of recursion, worthy of its own programming technique!
You can see this image along with a few other fun lake/island combinations at The Island and Lake Combination.