Previous Table of Contents Next


3.7.2. Attempted Sequential Results

A function returns only once. Suppose you have a list of numbers and want to compute the squares of each of them. It is tempting to write

   (define (squares nums)       ;; wrong!
     (if (null? nums)
         ‘()
         (* (car nums) (car nums))
         (squares (cdr nums))))

but this attempt to return the square of the first number and then, separately, to make a recursive call for the remaining numbers will fail. Instead, the first square must be combined with the result of the recursive call to construct a new list of numbers:

   (define (squares nums)
     (if (null? nums)
         ‘()
         (cons (* (car nums) (car nums))
               (squares (cdr nums)))))

This correct version uses cons to prepend the first square onto the list of squares generated by the recursive call.6


6A Scheme procedure can return multiple values, but even so, the values must be gathered together and returned all at once. A procedure using values looks just like a procedure that collects the values into one data structure. Values is a compiler efficiency feature, not a return to sequential programming.

3.7.3. Expressions Don’t Print

Sometimes the programmer thinks that a procedure prints its results. For example, beginners sometimes write

   (define (two-words sentence)        ;; wrong!
     (car sentence)
     (car (cdr sentence)))

expecting to use it this way:

   > (two-words ‘(hello goodbye))
   HELLO
   GOODBYE

In fact the procedure does not print anything; it merely computes values and returns the last of those values. Scheme’s interactive user interface prints the value of the expressions you type, which in this case means the value that two-words returns, so what really happens is this:

   > (two-words ‘(hello goodbye))
    GOODBYE

It is possible to tell Scheme explicitly to display a value, but this is almost always bad Scheme style.

3.7.4. No Variable Reassignment

Another mistake is thinking that an arithmetic operation changes the value of a variable. For example, here is an incorrect attempt to compute f(x) = 3x + 10:

   (define (f x)            ;; wrong!
     (* x 3)
     (+ x 10))

The correct version works by composition of functions:

   (define (f x)
     (+ (* x 3) 10))

3.7.5. Scheme Isn’t English

People who have programmed in other languages usually do not have this problem, but complete beginners sometimes try to make Scheme be English. For example, they may write

   (equal? argument (or ‘yes ‘no))     ;; wrong!

because they are thinking of the English question “Is the argument equal to yes or no?” Scheme does provide an or operation, but its arguments are true/false values, so the correct form is

(or (equal? argument ‘yes) (equal? argument ‘no))

3.7.6. Incomplete Expression Problems

Programmers unfamiliar with lambda sometimes try to use incomplete expressions as arguments to higher-order procedures, like this:

   (map (+ 5) ‘(6 7 8))    ;; wrong

instead of this:

   (map (lambda (x) (+ x 5)) ‘(6 7 8))

3.7.7. Recursion Problems

For programmers unaccustomed to recursion, there are many possible pitfalls. A recursive procedure must have a base case, and each recursive call must get closer, in some sense, to the base case. The value that the procedure returns in the base case must be in the range of the desired function. That sounds obvious, but sometimes when writing, for example, a procedure that returns a number, it’s easy to return an empty list in the base case without thinking about it because the six previously written procedures all returned lists.

3.7.8. cons Problems

The constructor cons that creates a list with one new element prepended to an argument list must have the new element first and then the list to be extended. Using (cons old-list new-element) doesn’t give an error message, but it creates a data structure that isn’t a valid list and looks funny when printed. In the thread manager earlier, to add a new element at the end of a list, I used this construction instead:

   (append old-list (list new-element))

3.8. References

Abelson, H., G. J. Sussman, and J. Sussman. 1996. Structure and interpretation of computer programs (2nd ed.). Cambridge, MA: MIT Press/McGraw-Hill. For more information, see http://www-mitpress.mit.edu/sicp/sicp.html.

Kelsey, R., W. Clinger, and J. Rees (Eds.). 1998. Revised5 report on the algorithmic language Scheme. Cornell University TR 92-1261. Available online from file://swissnet.ai.mit.edu/archive/scheme-reports/r5rs.ps.gz, along with much other documentation. Both of these have extensive bibliographies.


Previous Table of Contents Next