;; The ACCUMULATE procedure takes three arguments -- a procedure PROC of
;; arity 2, an initial value START, and a list LS -- and returns the result
;; of applying PROC first to the initial value and the first element of the
;; list, then to the result of the first step and the second element of the
;; list, then to the result of the second step and the third element of the
;; list, and so on. If LS is the null list, ACCUMULATE returns the initial
;; value unchanged.
(define accumulate
(lambda (proc start ls)
;; Test the preconditions.
(if (not (procedure? proc))
(error 'accumulate "The first argument must be a procedure"))
(if (not (list? ls))
(error 'accumulate "The third argument must be a list"))
;; Loop until the list is empty; at each stage, the new value of
;; SO-FAR is the result of applying PROC to the old value and the
;; first remaining element of the list.
(let loop ((so-far start)
(rest ls))
(if (null? rest)
so-far
(loop (proc so-far (car rest)) (cdr rest))))))
map.
;; The VARIANT-MAP procedure takes two arguments, of which the first must
;; be a list LS of procedures of arity 1, and returns a list whose elements
;; are the results of applying the members of LS to the second argument to
;; VARIANT-MAP.
(define variant-map
(lambda (procs arg)
;; Test the precondition.
(if (not ((list-of procedure?) procs))
(error 'variant-map
"The first argument must be a list of procedures"))
;; Use MAP to run down the list PROCS, applying each element to ARG.
(map (lambda (proc) (proc arg)) procs)))
;; The LIST-OF procedure takes as argument a predicate PRED? of arity 1 and
;; returns a predicate that determines whether its argument is a list, all
;; of the elements of which satisfy PRED?.
(define list-of
(lambda (pred?)
(letrec ((test-all (lambda (obj)
(or (null? obj)
(and (pair? obj)
(pred? (car obj))
(test-all (cdr obj)))))))
test-all)))
;; The procedure ITERATE takes two arguments, a natural number N and a
;; procedure F, and returns a procedure that is the Nth iterate of F --
;; in other words, a procedure that applies F to its argument, and then
;; applies F to the result of the first application, and then applies F
;; again to the result of the second application, and so on until F has
;; been applied N times. If N is 0, ITERATE returns the identity
;; procedure -- the procedure that always returns its argument unchanged.
(define iterate
(lambda (n f)
;; Test the preconditions.
(if (or (not (integer? n))
(negative? n))
(error 'iterate "The first argument must be a natural number"))
(if (not (procedure? f))
(error 'iterate "The second argument must be a procedure"))
;; Start with the identity procedure.
(let loop ((so-far (lambda (x) x))
(remaining n))
;; When the count of remaining iterations is reduced to zero,
;; return SO-FAR.
(if (zero? remaining)
so-far
;; If it is not yet zero, however, compose F with SO-FAR
;; once to get the next iterate and subtract one from the
;; count of remaining iterations.
(loop (compose f so-far) (- remaining 1))))))
;; The COMPOSE procedure takes as arguments two procedures, F and G, each
;; of arity 1, and returns a procedure of arity 1 that returns the result
;; of first applying G to its argument and then applying F to the result of
;; that first application.
(define compose
(lambda (f g)
;; Test the preconditions.
(if (or (not (procedure? f))
(not (procedure? g)))
(error 'compose "Both arguments must be procedures"))
;; Return a procedure that applies G and then F.
(lambda (x)
(f (g x)))))
This document is available on the World Wide Web as
http://www.math.grin.edu/courses/Scheme/spring-1997/solutions-3.html