Laboratory Exercises For Computer Science 153



Now that our toolbox contains local bindings to variables, we'll be seeing more and more cases in which it is useful to perform some procedure call or sequence of procedure calls repeatedly, for the sake of the side effects on structures or variables or for input or output. For example, the printing of the table of numbers and their square roots involved the repeated output of a line of a number and its root.

Most of these iterative constructions have a common form, and Scheme provides a special expression type to capture this form concisely and efficiently: the do-expression. A do-expression has the following structure:

(do loop-control-list
    (exit-test postlude)

Here's a simple example of a do-expression. We write a procedure display-countdown that takes one argument, a non-negative integer, and prints out the positive integers equal to or less than its argument, in descending order, one per line. Thus, the interaction might look as follows:

> (display-countdown 3)
Blast off!
The following code demonstrates a simple solution to this problem:
(define display-countdown
  (lambda (start)
  ;Pre-condition:  start is a nonnegative integer
  ;Post-condition:  prints the numbers start, ..., 1, 0 on separate lines
    (do ((remaining start (- remaining 1)))
        ((zero? remaining) "Blast off!")
      (display remaining)

Let's trace through the execution of a call to this procedure, using the above example (display-countdown 3):

  1. Write a procedure display-count that takes two arguments, start and finish, and counts upwards from start to finish, displaying each number on a separate line. (The preconditions are that start and finish must both be exact integers and start must be less than or equal to finish.) The display-count procedure should return the number of lines of output it produces.

  2. Review the sqrt-table procedure from the lab on input and output. This procedure takes one argument, a non-negative integer, and prints out a table of integers and their square roots, for integers equal to or less than its argument. In the previous lab, the code used a helper function to handle the printing of each line, after the initial header was printed. Replace the helper function by using a do-expression.

Let's look at another example of the use of do-expressions: Here is a procedure that takes a list as argument and returns the list in reverse order. That is, we write out explicit code for Scheme's reverse procedure.

(define reverse-list
  (lambda (ls)
  ;Pre-condition:  ls is a list
  ;Post-condition:  returns the elements of ls in reverse order on a list
    (if (list? ls)
        (do ((new-ls '() (cons (car old-ls) new-ls))
             (old-ls ls (cdr old-ls)))
            ((null? old-ls) new-ls)

> (reverse-list '(3 1 4 1 5 9))
(9 5 1 4 1 3)

This time there are two loop-control variables: new-ls provides a list of elements already processed (starting with nil, while old-ls contains the list elements not yet processed. The iteration moves an element from old-ls to new-ls as part of the updating of variables. the body of the do-expression is null.

  1. Define a Scheme procedure that takes any non-empty list of real numbers as its argument and returns the number that is the greatest element of the list. Use a do-expression to run through the positions of the list.

  2. Define a sum procedure, which takes any list of numbers as its argument and returns their sum. In your procedure, use a do-expression to process list elements iteratively.

  3. Write a procedure that counts the number of vowels that occur in a string. Within your procedure, keep the string in tact -- do not convert it to a list. Also, use a do-expression for any looping. Finally, keep the number of variables in your do-expression to a minimum -- for example, the string variable should not be redefined in the loop.

  4. Rewrite the encode-char procedure (including its kernel procedure) from the lab on strings, so that all looping is done using a do-expression.

Overall, do-expressions can be used for clarity and concision in many of the situations.

This document is available on the World Wide Web as

created April 21, 1997 by John David Stone
last revised January 10, 2000 by Henry M. Walker