1
\$\begingroup\$

Exercise 2.23

The procedure for-each is similar to map. It takes as arguments a procedure and a list of elements. However, rather than forming a list of the results, for-each just applies the procedure to each of the elements in turn, from left to right. The values returned by applying the procedure to the elements are not used at all -- for-each is used with procedures that perform an action, such as printing. For example,

(for-each (lambda (x) (newline) (display x))
          (list 57 321 88))
57
321
88

The value returned by the call to for-each (not illustrated above) can be something arbitrary, such as true. Give an implementation of for-each.

I wrote the following:

(define (for-each l f)
  (f (car l))
  (when (> (length l) 1) (for-each (cdr l) f)))

Is this a good answer?

\$\endgroup\$
3
\$\begingroup\$

No. Your function needs to handle the empty list too. If your implementation supports unless, use this:

(define (for-each f l)
  (unless (null? l)
    (f (car l))
    (for-each f (cdr l))))

Otherwise, the R5RS-compatible version would be:

(define (for-each f l)
  (cond ((not (null? l))
         (f (car l))
         (for-each f (cdr l)))))

Granted, the implicit begin behaviour of cond is not explained until Chapter 3. So if you want to avoid using that, here's a similar version that uses let to simulate begin:

(define (for-each f l)
  (cond ((not (null? l))
         (let ()
           (f (car l))
           (for-each f (cdr l))))))

Now, here, too, the let body is an implicit begin. If you want to work around that, too, then bind the result of the side-effect to a dummy variable:

(define (for-each f l)
  (cond ((not (null? l))
         (let ((unused (f (car l))))
           (for-each f (cdr l))))))
\$\endgroup\$
2
\$\begingroup\$

The solution below relies on neither unless nor begin. This seems somewhat desirable because neither of these procedures has been introduced in the text at this point.

(define (for-each proc items)
  (if (null? items)
    #t
    (if ((lambda (x) #t) (proc (car items)))
      (for-each proc (cdr items)))))
\$\endgroup\$
  • \$\begingroup\$ It's true that begin isn't introduced until chapter 3. However, (let () ...) (since let's body has an implicit begin) is a better workaround for that than using the (lambda (x) #t) hack that you have, since the latter doesn't make clear that (proc (car items)) is evaluated purely for side effects. \$\endgroup\$ – Chris Jester-Young Nov 11 '14 at 0:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.