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