2021-04-25 14:57:17 +02:00
|
|
|
(load "shared/util.scm")
|
|
|
|
(load "shared/sicp-evaluator.scm")
|
2021-01-25 14:42:19 +01:00
|
|
|
|
2021-01-26 19:31:52 +01:00
|
|
|
(display "\nex-4.25 - factorial-unless\n")
|
2021-01-25 14:42:19 +01:00
|
|
|
|
2021-01-26 19:31:52 +01:00
|
|
|
(eval '(define (unless condition usual-value exceptional-value)
|
|
|
|
(if condition exceptional-value usual-value)) the-global-environment)
|
|
|
|
|
|
|
|
(eval '(define (factorial n)
|
|
|
|
(unless (= n 1)
|
|
|
|
(* n (factorial (- n 1)))
|
|
|
|
1)) the-global-environment)
|
|
|
|
|
|
|
|
; This implementation of factorial is not going to terminate in regular
|
|
|
|
; applicative-order Scheme because the recursive calls to factorial result in
|
|
|
|
; an endless-loop. The definition would work in a normal-order language.
|
|
|
|
|
|
|
|
; (factorial 5)
|
|
|
|
|
|
|
|
(display "[answered]\n")
|
|
|
|
|
|
|
|
|
|
|
|
(display "\nex-4.26 - special-form-unless\n")
|
|
|
|
|
|
|
|
(define (unless-condition exp) (cadr exp))
|
|
|
|
(define (unless-usual exp) (caddr exp))
|
|
|
|
(define (unless-exceptional exp) (cadddr exp))
|
|
|
|
|
|
|
|
(define (unless->combination exp)
|
|
|
|
(make-if (unless-condition exp)
|
|
|
|
(unless-exceptional exp)
|
|
|
|
(unless-usual exp)))
|
|
|
|
|
|
|
|
(assert (eval '(factorial 3) the-global-environment) 6)
|
|
|
|
|
|
|
|
(display "\nexample - lazy-evaluation\n")
|
|
|
|
|
|
|
|
(define (eval exp env)
|
|
|
|
(cond ((self-evaluating? exp) exp)
|
|
|
|
((variable? exp) (lookup-variable-value exp env))
|
|
|
|
((quoted? exp) (text-of-quotation exp))
|
|
|
|
((assignment? exp) (eval-assignment exp env))
|
|
|
|
((definition? exp) (eval-definition exp env))
|
|
|
|
((if? exp) (eval-if exp env))
|
|
|
|
((lambda? exp)
|
|
|
|
(make-procedure (lambda-parameters exp)
|
|
|
|
(lambda-body exp)
|
|
|
|
env))
|
|
|
|
((begin? exp)
|
|
|
|
(eval-sequence (begin-actions exp) env))
|
|
|
|
((cond? exp) (eval (cond->if exp) env))
|
|
|
|
((application? exp)
|
|
|
|
(apply (actual-value (operator exp) env)
|
|
|
|
(operands exp)
|
|
|
|
env))
|
|
|
|
(else
|
|
|
|
(error "Unknown expression type -- EVAL" exp))))
|
|
|
|
|
|
|
|
(define (actual-value exp env)
|
|
|
|
(force-it (eval exp env)))
|
|
|
|
|
|
|
|
(define (apply procedure arguments env)
|
|
|
|
(cond ((primitive-procedure? procedure)
|
|
|
|
(apply-primitive-procedure
|
|
|
|
procedure
|
|
|
|
(list-of-arg-values arguments env))) ; changed
|
|
|
|
((compound-procedure? procedure)
|
|
|
|
(eval-sequence
|
|
|
|
(procedure-body procedure)
|
|
|
|
(extend-environment
|
|
|
|
(procedure-parameters procedure)
|
|
|
|
(list-of-delayed-args arguments env) ; changed
|
|
|
|
(procedure-environment procedure))))
|
|
|
|
(else
|
|
|
|
(error
|
|
|
|
"Unknown procedure type -- APPLY" procedure))))
|
|
|
|
|
|
|
|
(define (list-of-arg-values exps env)
|
|
|
|
(if (no-operands? exps)
|
|
|
|
'()
|
|
|
|
(cons (actual-value (first-operand exps) env)
|
|
|
|
(list-of-arg-values (rest-operands exps)
|
|
|
|
env))))
|
|
|
|
(define (list-of-delayed-args exps env)
|
|
|
|
(if (no-operands? exps)
|
|
|
|
'()
|
|
|
|
(cons (delay-it (first-operand exps) env)
|
|
|
|
(list-of-delayed-args (rest-operands exps)
|
|
|
|
env))))
|
|
|
|
|
|
|
|
(define (eval-if exp env)
|
|
|
|
(if (true? (actual-value (if-predicate exp) env))
|
|
|
|
(eval (if-consequent exp) env)
|
|
|
|
(eval (if-alternative exp) env)))
|
|
|
|
|
|
|
|
(define (force-it obj)
|
|
|
|
(if (thunk? obj)
|
|
|
|
(actual-value (thunk-exp obj) (thunk-env obj))
|
|
|
|
obj))
|
|
|
|
|
|
|
|
(define (delay-it exp env)
|
|
|
|
(list 'thunk exp env))
|
|
|
|
|
|
|
|
(define (thunk? obj)
|
|
|
|
(tagged-list? obj 'thunk))
|
|
|
|
|
|
|
|
(define (thunk-exp thunk) (cadr thunk))
|
|
|
|
|
|
|
|
(define (thunk-env thunk) (caddr thunk))
|
|
|
|
|
|
|
|
(define (evaluated-thunk? obj)
|
|
|
|
(tagged-list? obj 'evaluated-thunk))
|
|
|
|
|
|
|
|
(define (thunk-value evaluated-thunk) (cadr evaluated-thunk))
|
|
|
|
|
|
|
|
(define (force-it obj)
|
|
|
|
(cond ((thunk? obj)
|
|
|
|
(let ((result (actual-value
|
|
|
|
(thunk-exp obj)
|
|
|
|
(thunk-env obj))))
|
|
|
|
(set-car! obj 'evaluated-thunk)
|
|
|
|
(set-car! (cdr obj) result) ; replace exp with its value
|
|
|
|
(set-cdr! (cdr obj) '()) ; forget unneeded env
|
|
|
|
result))
|
|
|
|
((evaluated-thunk? obj)
|
|
|
|
(thunk-value obj))
|
|
|
|
(else obj)))
|
|
|
|
|
|
|
|
(define the-global-environment (setup-environment))
|
|
|
|
|
|
|
|
(eval '(define (unless condition usual-value exceptional-value)
|
|
|
|
(if condition exceptional-value usual-value)) the-global-environment)
|
|
|
|
|
|
|
|
(eval '(define (factorial n)
|
|
|
|
(unless (= n 1)
|
|
|
|
(* n (factorial (- n 1)))
|
|
|
|
1)) the-global-environment)
|
|
|
|
|
2021-01-27 16:08:51 +01:00
|
|
|
(assert (eval-force '(factorial 5) the-global-environment) 120)
|
2021-01-26 19:31:52 +01:00
|
|
|
|
|
|
|
(display "\nex-4.27 - lazy-evaluator\n")
|
|
|
|
|
|
|
|
(eval '(define count 0) the-global-environment)
|
2021-01-27 16:08:51 +01:00
|
|
|
(eval '(define (id x) (set! count (+ count 1)) x) the-global-environment)
|
2021-01-26 19:31:52 +01:00
|
|
|
(eval '(define w (id (id 10))) the-global-environment)
|
|
|
|
|
|
|
|
; I expected count to be 0 here, but it is 1 in reality. I think when
|
|
|
|
; list-of-delayed-args is called in the first call of id the count gets
|
|
|
|
; incremented the first time. Hence, the value is 1.
|
|
|
|
(assert (eval 'count the-global-environment) 1)
|
|
|
|
|
|
|
|
; w is thunk and cannot be displayed. By getting the actual value it will show
|
|
|
|
; 10 as expected.
|
2021-01-27 16:08:51 +01:00
|
|
|
(assert (eval-force 'w the-global-environment) 10)
|
2021-01-26 19:31:52 +01:00
|
|
|
|
|
|
|
; After w is fully evaluated count is 2 as expected.
|
|
|
|
(assert (eval 'count the-global-environment) 2) ; count is 2
|
|
|
|
|
2021-01-27 16:08:51 +01:00
|
|
|
(display "\nex-4.28 - eval-actual-value\n")
|
|
|
|
|
|
|
|
; Replace actual-value with eval in line 53 and see how this fails: unknown
|
|
|
|
; procedure (thunk + ...).
|
|
|
|
(assert (eval '((lambda (op a b) (op a b)) + 1 2) the-global-environment) 3)
|
|
|
|
|
|
|
|
(display "\nex-4.29 - memoization\n")
|
|
|
|
|
|
|
|
; Calculating fibonacci numbers recursively will run much slow without
|
|
|
|
; memoization.
|
|
|
|
(define the-global-environment (setup-environment))
|
|
|
|
(eval '(define (fib n)
|
|
|
|
(if (< n 2)
|
|
|
|
1
|
|
|
|
(+ (fib (- n 2)) (fib (- n 1)))))
|
|
|
|
the-global-environment)
|
|
|
|
(eval '(define count 0) the-global-environment)
|
|
|
|
(eval '(define (id x) (set! count (+ count 1)) x) the-global-environment)
|
|
|
|
(eval '(define (square x) (* x x)) the-global-environment)
|
|
|
|
|
|
|
|
(assert (eval '(square (id 10)) the-global-environment) 100)
|
|
|
|
(assert (eval 'count the-global-environment) 1)
|
|
|
|
(assert (eval '(fib 10) the-global-environment) 89)
|
|
|
|
|
|
|
|
; If we implemented force-it without memoization count would be 2 because the
|
|
|
|
; id-procedure gets evaluated twice:
|
|
|
|
; (assert (eval 'count the-global-environment) 2)
|
|
|
|
|
|
|
|
(display "\nex-4.30 - eval-sequence\n")
|
|
|
|
|
|
|
|
; a. Ben is right because primitive procedures are executed right away.
|
|
|
|
|
|
|
|
; b.
|
|
|
|
|
|
|
|
(eval
|
|
|
|
'(define (p1 x)
|
|
|
|
(set! x (cons x '(2)))
|
|
|
|
x)
|
|
|
|
the-global-environment)
|
|
|
|
|
|
|
|
(eval
|
|
|
|
'(define (p2 x)
|
|
|
|
(define (p e)
|
|
|
|
e
|
|
|
|
x)
|
|
|
|
(p (set! x (cons x '(2)))))
|
|
|
|
the-global-environment)
|
|
|
|
|
|
|
|
; With the text implementation the result is (1 2) because set! gets evaluated
|
|
|
|
; right away. p2 defines another procedure which has the consequence that e is
|
|
|
|
; evaluated lazily. Hence, the intial value of e 1 is returned.
|
|
|
|
|
|
|
|
(assert (eval-force '(p1 1) the-global-environment) '(1 2))
|
|
|
|
(assert (eval-force '(p2 1) the-global-environment) 1)
|
|
|
|
|
|
|
|
; If we change the implementation of eval-sequence the behavior of p1 is the
|
|
|
|
; same. However, since e gets evaluated right away in p2 the result is the same
|
|
|
|
; as for p1.
|
|
|
|
|
|
|
|
; c. Evaluating the statements right away does not change the behavior, because
|
|
|
|
; force-it is called either way.
|
|
|
|
|
|
|
|
; d. I think the lazy implementation should be kept as it is. Primitive
|
|
|
|
; procedures work as expected and if we have a lazy language it is consistent
|
|
|
|
; to not force the evaluation of unused thunks.
|
2021-01-26 19:31:52 +01:00
|
|
|
|
2021-01-27 16:08:51 +01:00
|
|
|
(display "[done]\n")
|
2021-01-25 14:42:19 +01:00
|
|
|
|