יואב רוזנברג 1 , פולקר מץ 2 ויבחר גנאור 1 מחלקה למדעי הגיאולוגיה והסביבה, אוניברסיטת בן גוריון
מבוא מורחב למדעי המחשב תרגול 13
description
Transcript of מבוא מורחב למדעי המחשב תרגול 13
המחשב למדעי מורחב מבוא13תרגול
Lazy Evaluation
Lazy Evaluation Implementation
• Two types of values– Delayed Values – Actual Values
• Introduce two functions– (actual-value exp env) returns real value– (delay-it exp env) returns a “thunk”
3
Lazy Evaluation• Normal order - delay operands (only)• Special forms and compound procedures may return
delayed objects • (l-eval exp env) may return delayed value• We use actual-value only if we have to,
otherwise we use l-eval• Primitives must receive the actual value• Print-on-screen is actual value
4
Lazy Evaluation – l-eval(define (l-eval exp env) (cond ((self-evaluating? exp) exp) ... ((application? exp (l-apply (actual-value (operator exp) env)
(operands exp) env))
(else (error "Unknown expression" exp))))
Compare with mc-eval:((application? exp) (mc-apply (mc-eval (operator exp) env) (list-of-values (operands exp) env)))
Lazy Evaluation – l-apply(define (l-apply procedure arguments env) ; changed (cond ((primitive-procedure? procedure) (apply-primitive-procedure
procedure (list-of-arg-values arguments env)))
((compound-procedure? procedure) (l-eval-sequence (procedure-body procedure)
(extend-environment (procedure-parameters procedure) (list-of-delayed-args arguments env)
(procedure-environment procedure)))) (else (error "Unknown proc" procedure))))
Actual vs. Delayed Values(define (actual-value exp env) (force-it (l-eval exp 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 (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))))
7
Representing Thunks
• In our implementation we represent “Thunks” using a tagged list:
thunk envexp
Thunks – abstractionConstructor:(define (delay-it exp env) (list 'thunk exp env))
Selectors:(define (thunk? obj) (tagged-list? obj 'thunk))(define (thunk-exp thunk) (cadr thunk))(define (thunk-env thunk) (caddr thunk))
(define (force-it obj) (cond ((thunk? obj) (actual-value (thunk-exp obj) (thunk-env obj))) (else obj)))
Lazy Evaluation – other changes needed
• Example – need actual predicate value in conditional if...(define (l-eval-if exp env) (if (true? (actual-value (if-predicate exp) env)) (l-eval (if-consequent exp) env) (l-eval (if-alternative exp) env)))
• Example – don't need actual value in assignment...
(define (l-eval-assignment exp env) (set-variable-value! (assignment-variable exp) (l-eval (assignment-value exp) env) env) 'ok)
Might return delayed values
No change relative to mc-eval version!
Lazy Evaluation – more changes(define input-prompt ";;; L-Eval input:")(define output-prompt ";;; L-Eval value:")
(define (driver-loop) (prompt-for-input input-prompt) (let ((input (read))) (let ((output (actual-value input the-global-environment))) (cond ((eq? output exit-object) 'done) (else (announce-output output-prompt) (user-print output) (driver-loop))))))
Lazy Evaluation - Example
• What happens when this code is executed using mc-eval?
• Can we add “unless” to the metacircular evaluator?
(define (unless condition usual exceptional) (if condition exceptional usual))
(define (factorial n) (unless (= n 1) (* n (factorial (- n 1))) 1))
12
Memoizing Thunks• Only evaluate
Thunks once
• Mutate our data structure when the thunk is first evaluated
thunk envexp
evaluated-thunk
result
13
Thunks – Memoizing Implementation (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) (set-cdr! (cdr obj) '()) result)) ((evaluated-thunk? obj) (thunk-value obj)) (else obj)))
Timing Comparison
• How would the different implementations (mc-eval, lazy, lazy + memoization) compute the following:
(define (square x) (* x x))(square (fib 100))
Example
• What is the result of (func 1)?• Is it similar to evaluation with mc-eval?
(define (func x) (define (p e) e x) (p (set! x (cons x '(2)))))
lazy and lazy-memo
• We want to extend the original mc-eval:
(lambda (a (b lazy) c (d lazy-memo)) ...)
– "a", "c" are strict variables (evaluated before procedure application
– "b" is lazy; it gets (re)-evaluated each time its value is actually needed
– "d" is lazy-memo; it gets evaluated the first time its value is needed, and then that value is returned again any other time it is needed again.
Controllably Memo-izing Thunks• thunk – never gets memoized• thunk-memo – first eval is remembered• evaluated-thunk – memoized-thunk that has
already been evaluated
whenforced
ThunkMemo
envexp
evaluated-thunk
result
A new version of delay-it
• Look at the variable declaration to do the right thing...
(define (delay-it decl exp env) (cond ((not (declaration? decl)) (l-eval exp env)) ((lazy? decl)
(list 'thunk exp env)) ((memo? decl)
(list 'thunk-memo exp env)) (else (error "unknown declaration:" decl))))
Changes to force-it(define (force-it obj) (cond ((thunk? obj) ;eval, but don't remember it (actual-value (thunk-exp obj) (thunk-env obj))) ((memoized-thunk? obj) ;eval and remember (let ((result (actual-value (thunk-exp obj) (thunk-env obj)))) (set-car! obj 'evaluated-thunk) (set-car! (cdr obj) result) (set-cdr! (cdr obj) '()) result)) ((evaluated-thunk? obj) (thunk-value obj)) (else obj)))
Changes to l-apply• Key: in l-apply, only delay "lazy" or "lazy-memo" params
– make thunks for "lazy" parameters– make memoized-thunks for "lazy-memo" parameters
(define (l-apply procedure arguments env) (cond ((primitive-procedure? procedure) ...) ; as before; apply on list-of-arg-values ((compound-procedure? procedure) (l-eval-sequence (procedure-body procedure)
(let ((params (procedure-parameters procedure))) (extend-environment
(map parameter-name params) (list-of-delayed-args params arguments env) (procedure-environment procedure)))))
(else (error "Unknown proc" procedure))))
Deciding when to evaluate an argument...
• Process each variable declaration together with application subexpressions – delay as necessary:
(define (list-of-delayed-args var-decls exps env) (if (no-operands? exps) '() (cons (delay-it (first-variable var-decls) (first-operand exps) env) (list-of-delayed-args (rest-variables var-decls)
(rest-operands exps) env))))
Syntax Extensions – Parameter Declarations
(define (first-variable var-decls) (car var-decls))(define (rest-variables var-decls) (cdr var-decls))(define declaration? pair?)
(define (parameter-name var-decl) (if (pair? var-decl) (car var-decl) var-decl))
(define (lazy? var-decl) (and (pair? var-decl) (eq? 'lazy (cadr var-decl))))
(define (memo? var-decl) (and (pair? var-decl) (eq? 'lazy-memo (cadr var-decl))))
Exam Question
• Assume we have the following special form:– (static <variable> <value>)
• Evaluation– If <variable> exists in the global environment,
do nothing– Otherwise evaluate <value> in the current
environment and bind <variable> to the result of the evaluation in the global environment
> (define (f) (static x 1) x)> (define (g x) (static x (* 2 2)) x)> (g 7)
> (f)
> (set! x (+ x 1))> (f)
7
4
5
How would the code evaluate?(define (id x) (static counter 0) (set! counter (+ counter 1)) x) (define (foobar counter) (id (begin (static counter 5) (id counter)))) (foobar 10) counter
GE id:
p:xb:…
foobar:
p:counterb:…
E1counter:10
E2x:10
counter:5/6
E3x:10
/7
mc-eval, static binding
counter7
(foobar 10)10
GE id:
p:xb:…
foobar:
p:counterb:…
E1counter:10
E2x:
/1
E3x:10
l-eval, static binding
thunk(begin…)
counter:0
In (driver-loop):(let ((output (actual-value input the-global-environment)))
/2
counter2
(foobar 10)10
GE id:
p:xb:…
foobar:
p:counterb:…
E1counter:10
E2x:10
counter:5
XX 11
E3x:10
XX 12
mc-eval, dynamic binding
counter5
(foobar 10)10
GE id:
p:xb:…
foobar:
p:counterb:…
E1counter:10
E2x:
counter:0
XX 11
E3x:11
XX 12
l-eval, dynamic binding
counter0
(foobar 10)11
thunk(begin…)
In (driver-loop):(let ((output (actual-value input the-global-environment)))