Previous Table of Contents Next


2.13.3. while, Continued

Now we return to our example of a while loop. Here is the function definition again, for review:

   (defun print-elements-of-list (list)
    “Print each element of LIST on a line of its own.”
    (while list
     (print (car list))
     (setq list (cdr list))))

The (car list) expression returns the first element of the variable list, if any, and print prints that element.

In the next line, (setq list (cdr list)), the Lisp interpreter interprets the (cdr list) expression first. This expression returns the second and subsequent elements of the list or an empty list if there are no such elements. setq then sets the variable list to this new value; it sets the value to a shorter list than it had before, to the old list minus its first element.

After the Lisp interpreter evaluates the last line of the body, it evaluates the while expression again. But this time the test expression, the first argument to while, has a different value than before; it has the value of the cdr of its previous value. It is a list still, but shortened by one element.

The looping continues until the variable list is an empty list. Then, when it is tested, the empty list, (), is returned. This is equivalent to nil and means false. At this point, the while loop stops looping and exits.

Here is an example:

   (setq animals ‘(gazelle lion tiger))

First, setq sets the variable animals to a list of three animals. (Presumably, the variable animals is a global variable, but we might have made it buffer local using the make-local-variable or make-variable-buffer-local functions.) We create and set this variable just to give us something to work with.

Next, we evaluate the function definition for print-elements-of-list; this creates (or loads) the definition.

Finally, we evaluate the expression:

   (print-elements-of-list animals)

which returns the following:

   gazelle
   lion
   tiger
   nil

Each element of the list is printed on a line of its own (that is what the function print does) and then the value returned by the function is printed. Because the last expression in the function is the while loop, and because while loops always return nil, a nil is printed after the last element of the list.

Here is what the while loop did:

1.  The while expression tested list, which at first had the value (gazelle lion tiger), which is non-nil, or true.
2.  The Lisp interpreter evaluated the first expression in the body, which printed the first element of the list, gazelle.
3.  Next, the Lisp interpreter evaluated the second expression in the body, which set the value of the variable to the cdr of the list, to (lion tiger).
4.  The loop looped. Each time the loop looped, the list became shorter.
5.  The list became an empty list, and the loop stopped.

Instead of cdring down a list, you can write a while loop with a numeric counter or some other mechanism that tests true as long as it should test true, and then tests false when the loop should stop.

For an easy introduction to while loops, see the section “while” in Programming in Emacs Lisp: An Introduction.

2.13.4. Recursion

A recursive function contains at least two expressions. In one of them, a test of the argument tells the Lisp interpreter to stop evaluating the function and leave it. In the other expression, the Lisp interpreter evaluates the same function, but provides it with a systematically different argument. The sequence of arguments passed to each call of the function changes in such a way that eventually a test of the argument tells the Lisp interpreter to stop evaluating the function and leave it.

For example, a function may continue to be called as long as a list has elements, but stop when the list is empty. Each call is on a shorter version of the list.

Here is such an example. This functions prints the elements of a list the while loop example did:

   (setq animals ‘(gazelle lion tiger))

   (defun print-elements-recursively (list)
    “Print each element of LIST on a line of its own.
   Uses recursion.”
     (print (car list))                    ; body
     (if list                              ; do-again-test
       (print-elements-recursively         ; recursive call
        (cdr list))))                      ; next-step-expression

   (print-elements-recursively animals)

The print-elements-recursively function first prints the first element of the list, the car of the list. Then, if the list is not empty, the function invokes itself, but gives itself as its argument, not the whole list, but the second and subsequent elements of the list, the cdr of the list.

When this evaluation occurs, the function prints the first element of the list it receives as its argument (which is the second element of the original list). Then the if expression is evaluated again, and when it evaluates to true, the function calls itself with the cdr of the list with which it is invoked. The second time around, this is the cdr of the cdr of the original list.

Each time the function invokes itself, it invokes itself on a shorter version of the original list. Eventually, the function invokes itself on an empty list. The print function prints the empty list as nil. Next, the conditional expression tests the value of list. Since the value of list is nil, the if expression tests false so the then part is not evaluated. The function as a whole then returns nil. Consequently, you see nil twice when you evaluate the function:

   gazelle
   lion
   tiger
   nil
   nil


Previous Table of Contents Next