Lecture 22

33
1 Lecture 22 Rehearsal: Lazy Evaluation Infinite Streams in our lazy evaluator

description

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. - PowerPoint PPT Presentation

Transcript of Lecture 22

  • Lecture 22Rehearsal: Lazy Evaluation Infinite Streams in our lazy evaluator

  • Well write a lazy interpreter.Our machine language is regular Scheme. I.e., it runs in applicative order, and its not lazy except for special cases.We will write a lazy interpreter, that translates the input code to instructions for the Non-lazy machine.

  • Thunks Abstractly a thunk is a "promise" to return a value when later needed ("forced")

  • 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 notwake up the arguments.

  • List-of-arg-values, list-of-delayed-args(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))))

  • Lets 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.

  • 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.

  • 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 streamsWe want to work with non-strict cons, car, cdr.We work with our lazy evaluator Running over the non-lazy Scheme. (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)))

    We define:

  • 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 wont need more examplesAnd we will also be running out of time

  • So we startThe outer loop gets the input: exp= (define ones (cons 1 ones))It calls:(actual-value (define ones (cons 1 ones)) GE)It calls:(force-it (l-eval (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.

  • Its 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 its 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))

  • So..We create the new environmentWe make the procedureWe assign the procedure value to the variable ones in GE define-variable! Returns okIts 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 its 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 is NOT a thunk, so we get: ones the 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

  • 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 againL-eval gets into action. It calls:

    (l-apply (actual-value (operator exp) E2)

    This forces m, and assigns m the procedure (lambda (p q) p)At this stage x,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))1We had to force the object twice!

  • Add-lists and the integersNow 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 languageTry to figure out what happens in the lower level with the Integers example.

    It is very difficult for humans to think in machine-languageIt is impossible for machines to think in upper language

    The interpreter (compiler) bridges the gap.

  • And another moral: it pays off to program wellThe 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.

    This is a top down, modular piece of software, built correctly with many layers of abstraction. However, since you havnt built an interpreter before, we will not start directly a top down review of the process but rather first give a simplified example.

    Note that the use of a high level description in the main eval loop allows easy modification For possible changes to the language, but also makes it harder to add new constructs since they must Be added by changing this main eveal loop.