Computer Science Logo Style volume 1: Symbolic Computing 2/e Copyright (C) 1997 MIT

How Recursion Works

cover photo
Brian Harvey
University of California, Berkeley

Download PDF version
Back to Table of Contents
BACK chapter thread NEXT
MIT Press web page for Computer Science Logo Style

The last two chapters were about how to write recursive procedures. This chapter is about how to believe in recursive procedures, and about understanding the process by which Logo carries them out.

Little People and Recursion

In Chapter 3, I introduced you to the metaphor of a computer full of little elves. Each elf is an expert on a particular procedure. I promised that this metaphor would be helpful later, when we'd have to think about two little people carrying out the same procedure at the same time. Well, "later" is now.

I want to use the elf metaphor to think about the downup example of the previous chapter:

to downup :word
print :word
if equalp count :word 1 [stop]
downup butlast :word
print :word
end

Recall that we are imagining the computer to be full of elves, each of whom is a specialist in carrying out some procedure. There are print elves, count elves, stop elves, and so on. Each elf has some number of pockets, used to hold the inputs for a particular invocation of a procedure. So a print elf will have one pocket, while an equalp elf needs two pockets.

We're going to be most interested in the downup elves and the contents of their pockets. To help you keep straight which elf is which, I'm going to name the downup elves alphabetically: the first one will be Ann, then Bill, then Cathy, then David, and so on. Since we aren't so interested in the other elves, I won't bother naming them.

»If you're reading this with a group of other people, you may find it helpful for each of you to take on the role of one of the downup elves and actually stick words in your pockets. If you have enough people, some of you should also serve as elves for the primitive procedures used, like print and if.

What happens when you type the instruction

downup "hello
to Logo? The Chief Elf reads this instruction and sees that it calls for the use of the procedure named downup. She therefore recruits Ann, an elf who specializes in that procedure. Since downup has one input, the Chief Elf has to give Ann something to put in her one pocket. Fortunately, the input you provided is a quoted word, which evaluates to itself. No other elves are needed to compute the input. Ann gets the word hello in her pocket.

Ann's task is to carry out the instructions that make up the definition of downup. The first instruction is

print :word
This, you'll remember, is an abbreviation for
print thing "word
Ann must hire two more elves, a print specialist and a thing specialist. The print elf can't begin his work until he's given something to put in his pocket. Ann asks the thing elf to figure out what that input should be. The thing elf also gets an input, namely the word word. As we saw in Chapter 3, word is what's written on the name tag in Ann's pocket, since word is the name of downup's input. So the thing elf looks in that pocket, where it finds the word hello. That word is then given to the print elf, who prints it on your computer screen.

Ann is now ready to evaluate the second instruction:

if equalp count :word 1 [stop]

Ann must hire several other elves to help her: an if elf, a count elf, and a thing elf. I won't go through all the steps in computing the inputs to if; since the count of the word hello is not 1, the first input to if turns out to be the word false. The second input to if is, of course, the list [stop]. (Notice that Ann does not hire a stop specialist. A list inside square brackets evaluates to itself, just like a quoted word, without invoking any procedures. If the first input to if had turned out to be true, it would have been the if elf who would have hired a stop elf to carry out the instruction inside the list.) Since its first input is false, the if elf ends up doing nothing.

Ann's third instruction is

downup butlast :word

Here's where things start to get interesting. Ann must hire another downup specialist, named Bill. (Ann can't carry out this new downup instruction herself because she's already in the middle of a job of her own.) Ann must give Bill an input to put in his pocket; to compute this input, she hires a butlast elf and a thing elf. They eventually come up with the word hell (the butlast of hello), and that's what Ann puts in Bill's pocket.

We now have two active downup elves, Ann and Bill. Each has a pocket. Both pockets are named word, but they have different contents: Ann's word pocket contains hello, while Bill's word pocket contains hell.

figure: elf2

Here is what this metaphor represents, in more technical language: Although there is only one procedure named downup, there can be more than one invocation of that procedure in progress at a particular moment. (An invocation of a procedure is also sometimes called an instantiation of the procedure.) Each invocation has its own local variables; at this moment there are two variables named word. It is perfectly possible for two variables to have the same name as long as they are associated with (local to) different procedure invocations.

If you had trouble figuring out how downup works in Chapter 7, it's almost certainly because of a misunderstanding about this business of local variables. That's what makes the elf metaphor so helpful. For example, if you're accustomed to programming in BASIC, then you're familiar with global variables as the only possibility in the language. If all variables were global in Logo, then there could only be one variable in the entire computer named word. Instead of representing variables as pockets in the elves' clothes, we'd have to represent them as safe deposit boxes kept in some central bank and shared by all the elves.

But even if you're familiar with Logo's use of local variables, you may have been thinking of the variables as being local to a procedure, instead of understanding that they are local to an invocation of a procedure. In that case you may have felt perfectly comfortable with the procedures named downup1, downup2, and so on, each of them using a separate variable named word. But you may still have gotten confused when the same variable word, the one belonging to the single procedure downup, seemed to have several values at once.

If you were confused in that way, here's how to use the elf metaphor to help yourself get unconfused: Suppose the procedure definitions are written on scrolls, which are kept in a library. There is only one copy of each scroll. (That is, there is only one definition for a given procedure.) All the elves who specialize in a particular procedure, like downup, have to share the same scroll. Well, if variables were local to a procedure, they'd be pockets in the scroll, rather than pockets in the elves' jackets. By directing your attention to the elves (the invocations) instead of the scrolls (the procedure definitions), you can see that there can be two variables with the same name (word), associated with the same procedure (downup), but belonging to different invocations (represented by the elves Ann and Bill).

We still have several more elves to meet, so I'm going to pass over some of the details more quickly now. We've just reached the point where Bill is ready to set to work. For his first instruction he hires a print elf, who prints the word hell on your screen. Why hell and not hello? The answer is that when Bill hires a thing expert to evaluate the expression :word, the thing rules say that that expert must look first in Bill's pockets, then (if Bill didn't have a pocket named word) in Ann's pockets.

Bill then carries out the if instruction, which again has no effect. Then Bill is ready for the downup instruction. He hires a third downup elf, named Cathy. Bill puts the word hel in Cathy's pocket. There are now three elves, all with pockets named word, each with a different word.

Cathy is now ready to get to work. Don't forget, though, that Ann and Bill haven't finished their jobs. Bill is still working on his third instruction, waiting for Cathy to report the completion of her task. Similarly, Ann is waiting for Bill to finish.

Cathy evaluates her first instruction, printing hel on the screen. She evaluates the if instruction, with no effect. Then she's ready for the downup instruction, the third one in the procedure definition. To carry out this instruction, she hires David, a fourth downup expert. She puts the word he in his pocket.

David's career is like that of the other downup elves we've met so far. He starts by printing his input, the word he. He evaluates the if instruction, with no effect. (The count of the word he is still not equal to 1.) He then gets to the recursive invocation of downup, for which he hires a fifth expert, named Ellen. He puts the word h in Ellen's pocket.

Ellen's career is not quite like that of the other elves. It starts similarly: she prints her input, the word h, on your screen. Then she prepares to evaluate the if instruction. This time, though, the first input to if turns out to be the word true, since the count of h is, indeed, 1. Therefore, the if elf evaluates the instruction contained in its second input, the list [stop]. It hires a stop elf, whose job is to tell Ellen to stop working. (Why Ellen? Why not one of the other active elves? There are seven elves active at the moment: Ann, Bill, Cathy, David, Ellen, the if elf, and the stop elf. The rule is that a stop elf stops the lowest-level invocation of a user-defined procedure. If and stop are primitives, so they don't satisfy the stop elf. The remaining five elves are experts in downup, a user-defined procedure; of the five, Ellen is the lowest-level invocation.)

(By the way, the insistence of stop on a user-defined procedure to stop is one of the few ways in which Logo treats such procedures differently from primitive procedures. If you think about it, you'll see that it would be useless for stop to stop just the invocation of if. That would mean that the if instruction would never do anything of interest and there would be no way to stop a procedure of your own conditionally. But you can imagine other situations in which it would be nice to be able to stop a primitive. Here's one:

repeat 100 [print "hello if equalp random 5 0 [stop]]

If it worked, this instruction would print the word hello some number of times, up to 100, but with a 20 percent chance of stopping after each time. In fact, though, you can't use stop to stop a repeat invocation.)

Let's review what's been printed so far:

hello   printed by Ann
hell    printed by Bill
hel     printed by Cathy
he      printed by David
h       printed by Ellen

Ellen has just stopped. She reports back to David, the elf who hired her. He's been waiting for her; now he can continue with his own work. David is up to the fourth and final instruction in the definition of downup:

print :word

What word will David print? For David, :word refers to the contents of his own pocket named word. That is, when David hires a thing expert, that expert looks first in David's pockets, before trying Cathy's, Bill's, and Ann's. The word in David's word pocket is he. So that's what David prints.

Okay, now David has reached the end of his instructions. He reports back to his employer, Cathy. She's been waiting for him, so that she can continue her own work. She, too, has one more print instruction to evaluate. She has the word hel in her word pocket, so that's what she prints.

Cathy now reports back to Bill. He prints his own word, hell. He reports back to Ann. She prints her word, hello.

When Ann finishes, she reports back to the Chief Elf, who prints a question mark on the screen and waits for you to type another instruction.

Here is the complete effect of this downup instruction:

hello   printed by Ann
hell    printed by Bill
hel     printed by Cathy
he      printed by David
h       printed by Ellen
he      printed by David
hel     printed by Cathy
hell    printed by Bill
hello   printed by Ann

»You might want to see if the little person metaphor can help you understand the working of the inout procedure from Chapter 7. Remember that each elf carrying out the recursive procedure needs two pockets, one for each input.

Tracing

Many people find the idea of multiple, simultaneous invocations of a single procedure confusing. To keep track of what's going on, you have to think about several "levels" of evaluation at once. "Where is downup up to right now?" -- "Well, it depends what you mean. The lowest-level downup invocation has just evaluated its first print instruction. But there are three other invocations of downup that are in the middle of evaluating their recursive downup instructions." This can be especially confusing if you've always been taught that the computer can only do one thing at a time. People often emphasize the sequential nature of the computer; what we've been saying about recursion seems to violate that nature.

If this kind of confusion is a problem for you, it may help to think about a procedure like downup by tracing its progress. That is, we can tell the procedure to print out extra information each time it's invoked, to help you see the sequence of events.

Just for reference, here's downup again:

to downup :word
print :word
if equalp count :word 1 [stop]
downup butlast :word
print :word
end

The trace command takes a procedure name (or a list of procedure names, to trace more than one) as its input. It tells Logo to notify you whenever that procedure is invoked:

? trace "downup
? downup "logo
( downup "logo )
logo
 ( downup "log )
log
  ( downup "lo )
lo
   ( downup "l )
l
   downup stops
lo
  downup stops
log
 downup stops
logo
downup stops

To make this result a little easier to read, I've printed the lines that are generated by the tracing in smaller letters than the lines generated by downup itself. Of course the actual computer output all looks the same.

Each line of tracing information is indented by a number of spaces equal to the number of traced procedure invocations already active--the level of procedure invocation. By looking only at the lines between one downup invocation and the equally-indented stopping line, you can see how much is accomplished by each recursive call. For example, the innermost invocation (at level 4) prints only the letter l.

Level and Sequence

The result of tracing downup is most helpful if you think about it two-dimensionally. If you read it vertically, it represents the sequence of instructions that fits the traditional model of computer programming. That is, the order of the printed lines represents the order of events in time. First the computer enters downup at level 1. Then it prints the word logo. Then it enters downup at level 2. Then it prints log. And so on. Each printed line, including the "official" lines as well as the tracing lines, represents a particular instruction, carried out at a particular moment. Reading the trace vertically will help you fit downup's recursive method into your sequential habits of thought.

On the other hand, if you read the trace horizontally, it shows you the hierarchy of levels of downup's invocations. To see this, think of the trace as divided into two overlapping columns. The left column consists of the official pattern of words printed by the original downup. In the right column, the pattern of entering and exiting from each level is shown. The lines corresponding to a particular level are indented by a number of spaces that corresponds to the level number. For example, find the line

 ( downup "log )

and the matching

 downup stops

Between these two lines you'll see this:

log
  ( downup "lo )
lo
   ( downup "l )
l
   downup stops
lo
  downup stops
log

What this shows is that levels 3 and 4 are part of level 2. You can see that the traced invocation and stopping lines for levels 3 and 4 begin further to the right than the ones for level 2. Similarly, the lines for level 4 are further indented than the ones for level 3. This variation in indentation is a graphic display of the superprocedure/subprocedure relationships among the various invocations.

There are two ways of thinking about the lines that aren't indented. One way is to look at all such lines within, say, level 2:

log
lo
l
lo
log

This tells you that those five lines are printed somehow within the activity of level 2. (In terms of the little people metaphor, those lines are printed by Bill, either directly or through some subordinate elf.) Another way to look at it is this:

 ( downup "log )
log
  ( downup "lo )
  ...
  downup stops
log
 downup stops

What this picture is trying to convey is that only the two log lines are directly within the control of level 2. The three shorter lines (lo, l, lo) are delegated to level 3.

We've seen three different points of view from which to read the trace, one vertical and two horizontal. The vertical point of view shows the sequence of events in time. The horizontal point of view can show either the total responsibility of a given level or the direct responsibility of the level. To develop a full understanding of recursion, the trick is to be able to see all of these aspects of the program at the same time.

»Try invoking the traced downup with a single-letter input. Make a point of reading the resulting trace from all of these viewpoints. Then try a two-letter input.

Instruction Stepping

Perhaps you are comfortable with the idea of levels of invocation, but confused about the particular order of instructions within downup. Why should the if instruction be where it is, instead of before the first print, for example? Logo's step command will allow you to examine each instruction line within downup as it is carried out:

? step "downup
? downup "ant
[print :word] >>>
ant
[if equalp count :word 1 [stop]] >>>
[downup butlast :word] >>>
[print :word] >>>
an
[if equalp count :word 1 [stop]] >>>
[downup butlast :word] >>>
[print :word] >>>
a
[if equalp count :word 1 [stop]] >>>
[print :word] >>>
an
[print :word] >>>
ant

After each of the lines ending with >>>, Logo waits for you to press the RETURN or ENTER key.

You can combine trace and step:

? step "downup
? trace "downup
? downup "ant
( downup "ant )
[print :word] >>>
ant
[if equalp count :word 1 [stop]] >>>
[downup butlast :word] >>>
 ( downup "an )
 [print :word] >>>
an
 [if equalp count :word 1 [stop]] >>>
 [downup butlast :word] >>>
  ( downup "a )
  [print :word] >>>
a
  [if equalp count :word 1 [stop]] >>>
  downup stops
 [print :word] >>>
an
 downup stops
[print :word] >>>
ant
downup stops

In this case, the step lines are indented to match the trace lines.

Once a procedure is traced or stepped, it remains so until you use the untrace or unstep command to counteract the tracing or stepping.

»Try drawing a vertical line extending between the line

 ( downup "an )

and the equally indented

 downup stops

Draw the line just to the left of the printing, after the indentation. The line you drew should also touch exactly four instruction lines. These four lines make up the entire definition of the downup procedure. If we restrict our attention to one particular invocation of downup, like the one you've marked, you can see that each of downup's instructions is, indeed, evaluated in the proper sequence. Below each of these instruction lines, you can see the effect of the corresponding instruction. The two print instructions each print one line in the left (unindented) column. (In this case, they both print the word an.) The if instruction has no visible effect. But the recursive invocation of downup has quite a large effect; it brings into play the further invocation of downup with the word a as input.

One way to use the stepping information is to "play computer." Pretend you are the Logo interpreter, carrying out a downup instruction. Exactly what would you do, step by step? As you work through the instructions making up the procedure definition, you can check yourself by comparing your activities to what's shown on the screen.

(back to Table of Contents)

BACK chapter thread NEXT

Brian Harvey, bh@cs.berkeley.edu