Monday, January 07, 2008

Scheme: Through the List Once

The simplest way to compute the average of the numbers in a list is:
  • compute the sum of the numbers in the list
  • compute the length of the list
  • divide the sum by the length, giving the average
So if we have sum and len defined as
(define (sum l) (foldl 0 + l))
(define (len l) (foldl 0 (lambda (x y) (+ 1 y)) l))
then we can compute the average as:
(define (two-pass-average l)
(cond
((null? l) 0)
(else (/ (sum l) (len l)))
)
)
This solution is correct, but it walks the list twice (once to compute the sum and once to compute the length). If we add a couple of accumulators and a facade function, we can compute the average with one list pass, like this:
(define (one-pass-average len-acc sum-acc l)
(cond
((and (null? l) (= 0 len-acc)) 0)
((null? l) (/ sum-acc len-acc))
(else (one-pass-average (+ 1 len-acc) (+ (car l) sum-acc) (cdr l)))
)
)
(define (average l) (one-pass-average 0 0 l))
The drawback to the one-pass method is that we've lost the nice definition of sum and len in terms of foldl. I wonder if we can restore it?

No comments: