Scheme's name for the third
procedure is caddr
.
It is described on page 16 of the Revised(4) report.
Exercises 2.21 and 3.5 in Scheme and the art of programming differ from the exercises that surround them in that in each case the simplest and most natural approach is unsatisfying. In exercise 2.21, the beginner is likely to write something like
which works, but may make the more sensitive and thoughtful students uneasy: Each recursive call begins by testing whether the operand is the empty list, even though this test is needed only for the initial call and cannot succeed otherwise. This is also the only exercise in chapter 2 in which the object that is passed to the recursive call (the cdr of(define all-same? (lambda (ls) (cond ((null? ls) #t) ((null? (cdr ls)) #t) ((equal? (car ls) (cadr ls)) (all-same? (cdr ls))) (else #f))))
ls
) is taken apart and partially examined before the recursive
call, which may make students faintly uncomfortable even though it is
correct. The solution to both problems is to use a helper procedure and an iterative process:
or, once the named let expression has been covered:(define all-same? (lambda (ls) (or (null? ls) (all-same?-helper (car ls) (cdr ls))))) (define all-same?-helper (lambda (fore aft) (or (null? aft) (and (equal? fore (car aft)) (all-same?-helper (car aft) (cdr aft))))))
In exercise 3.5, the problem is more serious. Students will tend to write(define all-same? (lambda (ls) (or (null? ls) (let loop ((fore (car ls)) (aft (cdr ls))) (or (null? aft) (let ((new-fore (car aft))) (and (equal? fore new-fore) (loop new-fore (cdr aft)))))))))
which unfortunately gives the wrong answer in the case of an unsuccessful search of a non-empty list, since 1 is added to the failure signal at the return from each recursive call. One can make the procedure shown above into a helper, invoked by an interface procedure only after a call to(define index (lambda (a ls) (cond ((null? ls) -1) ((equal? a (car ls)) 0) (else (add1 (index a (cdr ls)))))))
member?
has revealed that the search will be successful, but
this strikes me as an inefficient kludge. A coding like
seems slightly more elegant, but again will win no prizes for efficiency. The best solution is to use the iterative approach that is not explained until chapter 4:(define failure -1) (define add1-if-successful (lambda (result) (if (equal? result failure) failure (add1 result)))) (define index (lambda (a ls) (cond ((null? ls) failure) ((equal? a (car ls)) 0) (else (add1-if-successful (index a (cdr ls)))))))
or, more concisely,(define index-it (lambda (a ls level) (cond ((null? ls) -1) ((equal? a (car ls)) level) (else (index-it a (cdr ls) (add1 level)))))) (define index (lambda (a ls) (index-it a ls 0)))
(define (index a ls) (let loop ((rest ls) (level 0)) (cond ((null? rest) -1) ((equal? a (car rest)) level) (else (loop (cdr rest) (add1 level))))))
(define conjugate (lambda (z) (make-rectangular (real-part z) (- (imag-part z)))))
or, for extra credit,(define hypotenuse (lambda (leg-1 leg2) (sqrt (+ (* leg-1 leg-1) (* leg-2 leg-2)))))
(define hypotenuse (lambda (leg-1 leg-2) (magnitude (make-rectangular leg-1 leg-2))))
(rationalize 314/100 1/100)
=> 22/7, the ``simplest''
rational in the range from 313/100 to 315/100.
Appendix C4 of the IEEE standard presents essentially this algorithm, contributed by Alan Bawden and based on a discussion in G. H. Hardy and E. M. Wright, Introduction to the theory of numbers (New York: Oxford University Press; fifth edition, 1979).
(define rationalize (lambda (x epsilon) (simplest-in-range (r- x epsilon) (r+ x epsilon)))) (define simplest-in-range (lambda (bottom top) (cond ((r< top bottom) (simplest-in-range top bottom)) ((r= bottom top) bottom) ((rpositive? bottom) (simplest-in-range-helper bottom top)) ((rnegative? top) (rminus (simplest-in-range-helper (rminus top) (rminus bottom)))) (else (make-ratl 0 1))))) (define simplest-in-range-helper ; precondition: 0 < bottom < top (lambda (bottom top) (if (= (denr bottom) 1) bottom (let ((bottom-integer (rtruncate bottom)) (top-integer (rtruncate top))) (if (= bottom-integer top-integer) (r+ (make-ratl bottom-integer 1) (rinvert (simplest-in-range-helper (rinvert (rfrac top)) (rinvert (rfrac bottom))))) (make-ratl (add1 bottom-integer) 1)))))) (define rtruncate (lambda (rtl) (quotient (numr rtl) (denr rtl)))) (define rfrac (lambda (rtl) (make-ratl (remainder (numr rtl) (denr rtl)) (denr rtl))))
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/events/scheme-workshop/Monday-answers.html