Add exercise 4.30
This commit is contained in:
parent
6abfa8e373
commit
9d454b2a26
|
@ -0,0 +1,95 @@
|
|||
;; for-each is defined:
|
||||
;; (define (for-each (proc lazy) (items lazy))
|
||||
;; (if (null? items)
|
||||
;; 'done
|
||||
;; (begin (proc (car items))
|
||||
;; (for-each proc (cdr items)))))
|
||||
|
||||
|
||||
;; Part a: using the eval-sequence from leval handles sequenced side
|
||||
;; effects in for-each correctly. The example evaluates as follows:
|
||||
;; (for-each (lambda (x) (newline) (display x))
|
||||
;; (list 57 321 88))
|
||||
|
||||
;; (begin ((lambda (x) (newline) (display x)) 57)
|
||||
;; (begin ((lambda (x) (newline) (display x)) 321)
|
||||
;; (begin ((lambda (x) (newline) (display x)) 88))))
|
||||
|
||||
;; The first begin evaluates the first argument, which is an
|
||||
;; application, so it forces the procedure and delays its argument.
|
||||
;; Thus we have:
|
||||
|
||||
;; ((lambda (x) (newline) (display x)) '(thunk 57))
|
||||
|
||||
;; newline and display are both native procedures, so their arguments
|
||||
;; are forced, and 57 is displayed. The rest of the values follow
|
||||
;; similarly. The reason the normal-eval sequence works is that the
|
||||
;; procedure being applied for its side effect calls a primitive
|
||||
;; procedure.
|
||||
|
||||
;; Part b:
|
||||
|
||||
;; Define p1 and p2
|
||||
|
||||
;; (define (p1 (x lazy))
|
||||
;; (set! x (cons x '(2)))
|
||||
;; x)
|
||||
|
||||
;; (define (p2 (x lazy))
|
||||
;; (define (p (e lazy))
|
||||
;; e
|
||||
;; x)
|
||||
;; (p (set! x (cons x '(2)))))
|
||||
|
||||
;; Using the original evaluator:
|
||||
|
||||
;; (p1 1)
|
||||
;; -> (set! x (cons 1 '(2))) x
|
||||
;; ->(1 2)
|
||||
|
||||
;; because cons is primitive, so x is forced
|
||||
|
||||
;; (p2 1)
|
||||
;; -> (p '(thunk set! x (cons x '(2))) 1)
|
||||
;; -> 1
|
||||
|
||||
;; because nothing ever uses the argument to p, it is never forced.
|
||||
|
||||
;; An alternative eval-sequence that forces all expressions other than
|
||||
;; the last one to ensure all side effects are carried out.
|
||||
;; (define (eval-sequence* exps env)
|
||||
;; (cond ((last-exp? exps) (eval (first-exp exps) env))
|
||||
;; (else (actual-value (first-exp exps) env)
|
||||
;; (eval-sequence* (rest-exps exps) env))))
|
||||
|
||||
;; Using this:
|
||||
;; (p1 1)
|
||||
;; -> (1 2)
|
||||
|
||||
;; as before
|
||||
|
||||
;; (p2 1)
|
||||
;; -> (p '(thunk set! x (cons x '(2))))
|
||||
;; -> (actual-value '(thunk set! x (cons x '(2)))) (actual-value x)
|
||||
;; -> (1 2)
|
||||
|
||||
|
||||
;; Part c:
|
||||
|
||||
;; Using eval-sequence*
|
||||
|
||||
;; (for-each (lambda (x) (newline) (display x))
|
||||
;; (list 57 321 88))
|
||||
|
||||
;; -> (begin ((lambda (x) (newline) (display x)) 57)
|
||||
;; (begin ((lambda (x) (newline) (display x)) 321)
|
||||
;; (begin ((lambda (x) (newline) (display x)) 88))))
|
||||
|
||||
;; This evaluates as for eval-sequence because the procedure only has
|
||||
;; native procedures which are forced anyway, so there is no
|
||||
;; difference between the two options.
|
||||
|
||||
;; Part d: I prefer eval-sequence*, Cy's approach, as sequences are
|
||||
;; used for side effects (since only the value of the final
|
||||
;; expression is returned). So ensuring that side-effects are handled
|
||||
;; correctly seems like a better option.
|
Loading…
Reference in New Issue