Patterns

One of the pleasant features of programming is that it is often possible to write some procedure that you need by making small changes in an existing procedure -- either one that you yourself wrote, or one that you encountered in a procedure library or a textbook. An experienced programmer recognizes patterns -- structural similarities between algorithms -- that can be applied repeatedly, with variations.

For instance, the procedure for doubling each element on a given list of numbers and the procedure for listing the lengths of the strings on a given list have a common structure:

(define double-each-element
  (lambda (ls)
    (if (null? ls)
        '()
        (cons (* 2 (car ls)) (double-each-element (cdr ls))))))

(define length-of-each-element
  (lambda (ls)
    (if (null? ls)
        '()
        (cons (string-length (car ls)) (length-of-each-element (cdr ls))))))

Once one has seen and understood both of these, it is not much of a stretch to figure out how to define a procedure that takes reverses each list on a list of lists and returns a list of the reversed lists:

(define reverse-each-element
  (lambda (ls)
    (if (null? ls)
        '()
        (cons (reverse (car ls)) (reverse-each-element (cdr ls))))))

Similarly, we have seen a procedure for constructing a list containing a given number of repetitions of a given value:

(define replicate
  (lambda (count value)
    (if (zero? count)
        '()
        (cons value (replicate (- count 1) value)))))

and a similar procedure for constructing a string containing a given number of repetitions of a given string:

(define replicate-string
  (lambda (count template)
    (if (zero? count)
        ""
        (string-append template (replicate-string (- count 1) template)))))

so it is not difficult to come up with the somewhat similar procedure for ``wrapping'' a given number of pairs of parentheses around a given value, by repeated applications of list:

(define wrap-up
  (lambda (count value)
    (if (zero? count)
        value
        (list (wrap-up (- count 1) value)))))

> (wrap-up 5 'foo)
(((((foo)))))

The common pattern in this case is that the numerical parameter counts down the number of times that some simple operation remains to be applied; in the if-expression, the test checks whether this countdown has reached zero, the consequent supplies the base-case value of the procedure, and the alternative applies the simple operation once to the slightly simpler result returned by the recursive call.

A third pattern is list recursion with a conditional test on each element, as in filter-out-negatives --

(define filter-out-negatives
  (lambda (ls)
    (cond ((null? ls) '())
          ((negative? (car ls)) (filter-out-negatives (cdr ls)))
          (else (cons (car ls) (filter-out-negatives (cdr ls)))))))

-- or as in the following definition of product-of-odd-elements, which takes any list of integers and determines the product of just those elements that are odd:

(define product-of-odd-elements
  (lambda (ls)
    (cond ((null? ls) 1)
          ((odd? (car ls)) (* (car ls) (product-of-odd-elements (cdr ls))))
          (else (product-of-odd-elements (cdr ls))))))

A slight variation of this pattern is found in the definition of the following definition of the all-symbols? predicate, which takes any list and determines whether it contains nothing but symbols as elements:

(define all-symbols?
  (lambda (ls)
    (cond ((null? ls) #t) ; Vacuously, an empty list has nothing but symbols.
          ((symbol? (car ls)) (all-symbols? (cdr ls)))
          (else #f))))

In this case, the else-clause need not contain a recursive call, because we know what the result must be without even looking at the subsequent elements of ls.


created February 8, 1998 by John David Stone (stone@cs.grinnell.edu)
last revised January 18, 2004 by Henry M. Walker at walker@cs.grinnell.edu.
Validated as XHTML 1.0 by the World Wide Web Consortium Cascading Style Sheet validated by the World Wide Web Consortium
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu.