<< Previous exercise (2.22) | Index | Next exercise (2.24) >>
(define (for-each proc items)
(let ((items-cdr (cdr items)))
(proc (car items))
(if (not (null? items-cdr))
(for-each proc items-cdr)
true)))
;; I didn't find that any of the other implementations here ;; supported the null list as input; here's my fix. ;; for-each (define (for-each proc list) (cond ((null? list) #t) (else (proc (car list)) (for-each proc (cdr list)))))
;; for-each ;; The below didn't work ... basically, I needed some kind of block ;; structure, since if has the form (if (test) true-branch ;; false-branch). I needed to have true-branch execute the proc, then ;; call the next iteration of for-each, and the only way I knew how to ;; do that was with brackets ... but of course that doesn't work, as ;; the interpreter tries to apply the result of the first proc call as ;; a function to the rest. (define (for-each proc items) (if (not (null? items)) ((proc (car items)) (for-each proc (cdr items))))) (for-each (lambda (x) (newline) (display x)) (list 1 2 3 4)) ;; This one works. ;; Moral: cond is better for multi-line branches. (define (for-each proc items) (cond ((not (null? items)) (proc (car items)) (for-each proc (cdr items)))))
You can use begin https://stackoverflow.com/a/11263539/21294350 although we should not take too much time to learn Scheme syntax.
Also see Tom's comment.
;;; First, I'm sorry for using common lisp. ;;; This is an easy but funny problem, At a first glance, I think I need some ;;; block structure such as cond, progn, etc., in fact, you can use function ;;; argument eval rule to avoid using them at all. I think this solution has ;;; more 'functional style'. (defun for-each (f items) (labels ((iter (action lst) (if (null lst) action (iter (funcall f (car lst)) (cdr lst))))) (iter nil items)))
Like tyg's answer but using Scheme.
; Simply iterate with a parameter solely for evaluating the procedure with the current argument. ; In accordance with the function evaluation rule, this parameter will be evaluated each time. ; The parameter is not used in the function body -- it is 'discarded'. ; The initial iteration passes #t to this parameter, which has no effect. (define (for-each procedure items) (define (iter items evaluate) (if (null? items) #t (iter (cdr items) (procedure (car items))))) (iter items #t))
Reference: http://clhs.lisp.se/Body/s_progn.htm progn "evaluates forms, in the order".
https://jtra.cz/stuff/lisp/sclr/labels.html labels is similar to let in Scheme.
; :) (define (for-each proc items) (map proc items) #t)
Same idea but an explicit distort function.
(define (for-each procedure list_) (define (distort x) #t) (distort (map procedure list_)) ) (for-each (lambda (x) (newline) (display x)) (list 57 321 88))
The above solution by amasad is incorrect as MAP is different to FOR-EACH. Map will create a LIST of the results whereas we don't want the output of For-each to create a list, just to apply the specified procedure to each of the list elements.
The solution by amasad is perfectly fine. It returns true, it doesn't matter if internally it "creates a list" or whatever. In fact, I'd say this'd be fine too:
(define for-each map)
as the exercise states that the result can be arbitrary, so it can arbitrarily be the list of items in the list applied to the given procedure.
I think this solution is incorrect not because of AMS's reason, but rather because the implementation of MAP can vary, and it isn't guaranteed that the procedure that is given to MAP will be applied left-to-right (see this in the R5RS specification: "The dynamic order in which proc is applied to the elements of the lists is unspecified").
;;; i did the same with common-lisp ;;; it pretty much matches the first scheme form on this page tho! (defun for-each (proc items) (if (null items) nil (apply proc (car items) nil)) (if (not (null (cdr items))) (for-each proc (cdr items))))
;; Another way to use if instead of cond. ;; We just wrap the multiple statements in a let. ;; From: http://wiki.drewhess.com/wiki/SICP_exercise_2.23 (define (for-each proc items) (if (not (null? items)) (let () (proc (car items)) (for-each proc (cdr items)))))
(define (for-each unary-proc seq)
(if (null? seq)
(newline)
(unary-proc (car seq)) (for-each unary-proc (cdr seq)) ) )
This was my solution, kind of weird but it worked on the example in the book.
(define (for-each f items)
(define (iter f items . cur)
(if (null? items)
#t
(iter f
(cdr items)
(f (car items)))))
(iter f items))
May be not so concise as other solutions. But without let and other advanced features, very straightforward. The "block structure" mentioned by jz can be achieved by a function, as we have learnt from previous chapter (procedural abstraction).
(define (for-each proc items)
(if (null? items)
nil
((lambda (li) (proc (car li)) (for-each proc (cdr li))) items)))
And in the first answer, do not see why a return value true is necessary. The following one will not return or print a #t as the end.
(define (for-each-an1 proc items)
(let ((items-cdr (cdr items)))
(proc (car items))
(if (not (null? items-cdr))
(for-each-an1 proc items-cdr)
)))
Not really sure if this is an elegant solution but it seems much cleaner than all the other solutions that have been posted. Using the print procedure given as an example, if the input is the empty list then it prints that, otherwise it prints every element of the list excluding the empty list. It works because it first checks whether items is the empty list before reaching ahead to the cdr of items. I don't see any other way to prevent passing a non-pair to cdr and still treat nil as the list terminator.
(define (for-each proc items)
(cond ((null? items) (proc items))
((null? (cdr items)) (proc (car items)))
(else (proc (car items)) (for-each proc (cdr items)))))
As the exercise states "It takes as arguments a procedure and a list of elements.", so we don't need to care about "passing a non-pair to cdr".
I use lambda compose two statement in else branch
(define (for-each proc list)
(if (null? list)
'()
((lambda ()
(proc (car list))
(for-each proc (cdr list))))))
This solution avoids advanced language features and illustrates a general problem solving approach: First, think of a precondition which makes the problem easy to solve (in this exercise, when the list is nonempty). Then solve the general case by trying to satisfy the precondition.
(define (for-each proc list)
; precondition: list is nonempty.
(define (for-each-nonempty list)
(proc (car list))
(if (null? (cdr list))
'()
(for-each-nonempty (cdr list))))
; general case: ensure precondition.
(if (null? list)
'()
(for-each-nonempty list)))
take advantage of the fact that each cond clause may be a sequence of expressions
(define (for-each fxn items)
(cond ((null? items) #t)
(else (fxn (car items)) (for-each fxn (cdr items)))))
Clean and no empty list returned
(define (for-each proc items)
(cond ((null? items) nil)
((null? (cdr items)) (proc (car items)))
(else (proc (car items)) (for-each proc (cdr items)))))
Summary history comments (interesting, it seems that each last exercise in one exercise block like exercise 2.21~2.23 will have one long comment list):
jz's comment share the same ideas as sritchie's but with the different returned value. Same ideas as jz's: anonymous's, coriolis's with the wrong syntax and gustave's. master's is similar but have one redundant 2nd case since we only need base and induction.
tyg's uses argument to call func instead of explicitly calling it. erik's is similar but with the variable argument number.
Tom's is same as the code after simv's. wilson's is same as Tom's.
amasad's comment just calls map and discards the returned value although wrong. otakutyrant's shares tyg's ideas.
See justinm's comment for how to think about this exercise although it may be trivial. Same as master's: justinm's and svelty's.
why complicate use "and" and output some dummy return value
(define (for-Each proc items) (if (null? items) #t (and (proc (car items)) (for-Each proc (cdr items))) ))
simv
I think the above solution by madschemer has a bug. If the procedure you pass in returns #f it won't recurse down the list, because the call to "and" will be short-circuited.
LisScheSic
You are right.
Actually we can mimic the book implementation for stream corresponding part (see section 3.5.1) by removing all stream- prefixes.
(define (stream-for-each proc s) (if (stream-null? s) 'done (begin (proc (stream-car s)) (stream-for-each proc (stream-cdr s)))))