SELECTIVE TALLYING

Problem: Given a collection of values and a predicate (of arity 1) that any of the values might or might not satisfy, determine the number of values that do in fact satisfy the predicate.

We'll assume that the values are collected in a list. Here are three divergent approaches that indicate something about the range of programming styles in Scheme.

The first is a classical recursion in which successive instances of the same problem differ with respect to two parameters: rest, the value of which is the part of the list containing the elements that have not yet been examined, and tally, the value of which is the number of elements, among those that have already been examined, that satisfy the predicate.

The advantage of carrying tally along as a parameter is that the recursive call becomes a ``tail'' recursion (one in which the value of returned from a call is forwarded without further computation to the next caller down). An implementation of Scheme is required to optimize tail-recursive calls.

The ``named let'' construction provides a convenient way to define and invoke the local recursive procedure, loop, with appropriate initial values for the parameters.

(define selective-tally
  (lambda (values predicate)
    (let loop ((rest values)
               (tally 0))
      (if (null? rest)
          tally
          (loop (cdr rest)
                (if (predicate (car rest))
                    (+ tally 1)
                    tally))))))
The second version exploits Scheme's support for a functional programming style. The idea is to define a mapping (here called weight) that takes every value that satisfies the predicate to 1 and every value that does not satisfy the predicate to 0. One then uses Scheme's map procedure to obtain the image of the list of values under that mapping, and applies the + procedure to the resulting list to obtain its sum.
(define selective-tally
  (lambda (values predicate)
    (let ((weight
           (lambda (object)
             (if (predicate object) 1 0))))
      (apply + (map weight values)))))
The third version is written in the procedural style, with explicit iteration and assignment.
(define selective-tally
  (lambda (values predicate)
    (do ((rest values (cdr rest))
         (tally 0))
        ((null? rest) tally)
      (if (predicate (car rest))
          (set! tally (+ tally 1))))))


This document is available on the World Wide Web as

http://www.math.grin.edu/~stone/events/scheme-workshop/tallying.html


created July 8, 1995
last revised June 24, 1996

John David Stone (stone@math.grin.edu)