330 likes | 500 Views
Lecture 22. Rehearsal: Lazy Evaluation Infinite Streams in our lazy evaluator. We’ll write a lazy interpreter. Our machine language is regular Scheme. I.e., it runs in applicative order, and it’s not lazy except for special cases.
E N D
Lecture 22 Rehearsal: Lazy Evaluation Infinite Streams in our lazy evaluator
We’ll write a lazy interpreter. Our machine language is regular Scheme. I.e., it runs in applicative order, and it’s not lazy except for special cases. We will write a lazy interpreter, that translates the input code to instructions for the Non-lazy machine.
Concretely – our representation: thunk exp env Thunks • Abstractly – a thunk is a "promise" to return a value when later needed ("forced")
thunk exp env Thunks- implementation (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))
Forcing a thunk. (define (force-it obj) (cond ((thunk? obj) (actual-value (thunk-exp obj) (thunk-env obj))) (else obj))) (define (actual-value exp env) (force-it (l-eval exp env)))
L-apply (define (l-apply procedure arguments env) (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)))) Notice that l-apply sends all arguments of Compound procedures to sleep.
L-Eval (define (l-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) (l-eval (cond->if exp) env)) ((application? exp) (l-apply (actual-value (operator exp) env) (operands exp) env)) (else (error "Unknown expression type – l-EVAL" exp)))) Undelying procedures (like eval-definition) call l-eval instead of eval. L-eval does not wake up the arguments.
List-of-arg-values, list-of-delayed-args (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))))
thunk exp env Let’s take an example (define d1 (delay-it exp env)) d1 is a pointer to a list. Exp is an abstract expression, and it is not evaluated.
answer thunk exp env What does l-eval do with a thunk? (l-eval d1) It returns the address of d1, I.e., a pointer to the list. Exp is an abstract expression, and it is not evaluated.
thunk exp env What does force-it do with a thunk? (force-it d1) (actual-value exp env) It evalutes exp in the environment env.
Taking a bigger example: infinite streams We want to work with non-strict cons, car, cdr. We work with our lazy evaluator Running over the non-lazy Scheme. We define: (define (cons x y) (lambda (m) (m x y))) (define (car z) (z (lambda (p q) p))) (define (cdr z) (z (lambda (p q) q)))
Our example; the ones stream. (define ones (cons 1 ones)) (car ones) We will do it in detail. We will be so tired after that, that we won’t need more examples… And we will also be running out of time…
So we start… The outer loop gets the input: exp= “(define ones (cons 1 ones))” It calls: (actual-value ‘(define ones (cons 1 ones)) GE) The actual-value from the outer loop, is the one sending the force, which wakes up all the sleeping values that are needed. It calls: (force-it (l-eval ‘(define ones (cons 1 ones)) GE))
It’s a definition, so… (force-it (eval-definition ‘(define ones (cons 1 ones)) GE)) (force-it (define-variable! (definition-variable ‘(define..)) (l-eval (definition-value ‘(define ..)) GE) GE)) (force-it (define-variable! ‘ones (l-eval ‘(cons 1 ones) GE) GE))
Eval again, and it’s … a procedure (force-it (define-variable! ‘ones (l-apply (actual-value ‘cons GE) ‘(1 ones) GE)) GE)) (actual-value ‘cons GE) (force-it (l-eval ‘cons GE)) (force-it #proc-..) #proc-.. (force-it (define-variable! ‘ones (l-apply #proc-cons ‘(1 ones) GE)) GE))
L-apply gets into action… (force-it (define-variable! ‘ones (l-eval-sequence ((lambda (m) (m x y))) (extend-environment ‘(x y) (list-of-delayed-args ‘(1 ones) GE) GE)) GE))
The whole point: the arguments are delayed. (force-it (define-variable! ‘ones (l-eval-sequence ((lambda (m) (m x y))) (extend-environment ‘(x y) (list (delay-it ‘1 GE) (delay-it ‘ones GE)) GE)) GE))
I.e., the arguments are thunks. (force-it (define-variable! ‘ones (l-eval (lambda (m) (m x y)) (extend-environment ‘(x y) (list (list 'thunk ‘1 GE) (list ‘thunk ‘ones GE)) GE)) GE))
L-eval again. This time.. A procedure (force-it (define-variable! ‘ones (make-procedure (lambda-parameters ‘(lambda (m) (m x y))) (lambda-body ‘(lambda (m) m x y))) (extend-environment ‘(x y) (list (list 'thunk ‘1 GE) (list ‘thunk ‘ones GE)) GE)) GE))
GE ones: E1 x: (list 'thunk ‘1 GE)y: (list ‘thunk ‘ones GE)) p: mbody: (m x y) So.. • We create the new environment • We make the procedure • We assign the procedure value to the variable ‘ones in GE • define-variable! Returns ‘ok • It’s not a thunk so force-it returns it as well.
Now we try ‘(car ones) (actual-value ‘(car ones) GE) (force-it (l-eval ‘(car ones) GE)) (force-it (l-apply #proc-car ‘ones GE)) (force-it (l-eval-sequence ‘((z (lambda (p q) p))) (extend-environment ‘(z) (list-of-delayed-args ‘ones GE) GE)))
L-eval gets ready.. (force-it (l-eval ‘(z (lambda (p q) p)) (extend-environment ‘(z) (list (list 'thunk ‘ones GE)) GE))) l-eval looks at the expression, and it’s a compound procedure.
L-eval moves (force-it (l-eval (l-apply (actual-value ‘z (extend-env ..)) ‘(lambda (p q) p) (extend-environment ‘(z) (list (list 'thunk ‘ones GE)) GE)))
(actual-value ‘z (extend-env ..)) Actual-value opens to: (force-it (l-eval ‘z (extend-environment ‘(z) (list (list 'thunk ‘ones GE)) GE))) (force-it (list ‘thunk ‘ones GE)) (actual-value ‘ones GE) It isNOTa thunk, so we get:onesthe address of The procedure ones.
Back to L-eval (force-it (l-eval (l-apply ones ‘(lambda (p q) p) (extend-environment …))) We move the ball to l-apply…
E2 L-eval again.. (force-it (l-eval (l-eval ‘(m x y) (extend-environment ‘(m) (list-of-delayed-args ‘(lambda (p q) p) (extend-env…)) (extend-environment ‘(x y) (list (list 'thunk ‘1 GE) (list ‘thunk ‘ones GE)) GE))))))
And l-eval again… L-eval gets into action. It calls: (l-apply (actual-value (operator exp) E2) … This forcesm, and assigns m the procedure(lambda (p q) p) At this stagex,y are still delayed. We now get: (l-apply (lambda (p q) p) ‘(x y) E2)
And l-apply again… (l-eval ‘p (extend-environment ‘(p q) (list-of-delayed-args ‘(x y) E2) E2)) In the extended environment E2, p gets bounded to (list ‘thunk x E1) And so this is what l-eval returns. (list ‘thunk x E1)
We know force it. Obj = (list ‘thunk x E1) (actual-value (thunk-exp obj) (thunk-env obj))) (actual-value x E1) (force-it (l-eval x E1)) (force-it (list ‘thunk ‘1 GE)) ‘1 We had to force the object twice!
Add-lists and the integers Now consider: (define (add-lists list1 list2) (cond ((null? List1) list2) ((null? List2) list1) (else (cons (+ (car list1) (car list2)) (add-lists (cdr list1) (cdr list2)))))) (define ones (cons 1 ones)) (define integers (cons 1 (add-lists one integers))
Upper vs. lower language • Try to figure out what happens in the lower level with the • Integers example. • It is very difficult for humans to think in machine-language • It is impossible for machines to think in upper language • The interpreter (compiler) bridges the gap.
And another moral: it pays off to program well • The actual process that takes place when evaluating integers, • Is techniquly complicated. • A process can be techniqly complicated but still (very) • efficient. • With careful planning, complicated processes can have a • Short, • Concise • Efficient • implementation as a sequence of simple well-defined procedures.