Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 Lecture 19 Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)

Similar presentations


Presentation on theme: "1 Lecture 19 Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)"— Presentation transcript:

1

2 1 Lecture 19 Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)

3 2 Last lecture: Lexical Scoping Free variables in an application of procedure f get their values from the procedure where f was defined (by the appropriate lambda special form). Also called static binding (define (foo x y) (lambda (z) (+ x y z))) (define bar (foo 1 2)) (bar 3) bar : (lambda (z) (+ 1 2 z))

4 3 Lexical Scoping Environment Diagram (define (foo x y) (lambda (z) (+ x y z))) GE p: x y body: ( (z) (+ x y z)) foo: E1 x: 1 y: 2 bar: p: z body: (+ x y z) (+ x y z) | E2 => 6 z: 3 E2 (define bar (foo 1 2)) (bar 3) Will always evaluate (+ x y z) in a new environment inside the surrounding lexical environment.

5 4 Alternative Model: Dynamic Scoping Dynamic scope: –Look up free variables in the caller's environment rather than the surrounding lexical environment Example: (define x 11) (define bear (lambda (y) (+ x y))) (define (p x) (bear 20)) (p 9) => 29

6 5 Dynamic Scoping Environment Diagram (define (p x) (bear 20)) GE p: x body: (bear  p: (+ x y) | E2 => 29 x: 9 E1 (define (bear y) (+ x y)) (p 9) Will evaluate (+ x y) in an environment that extends the caller's environment. bear: p: y body: (+ x y  y: 20 E2 (define x 11) x:11

7 6 A "Dynamic" Version of Scheme (define (d-eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env))... ((lambda? exp) (make-procedure (lambda-parameters exp) (lambda-body exp) '*no-environment*)) ;CHANGE: no env... ((application? exp) (d-apply (d-eval (operator exp) env) (list-of-values (operands exp) env) env)) ;CHANGE: add env (else (error "Unknown expression -- M-EVAL" exp))))

8 7 A "Dynamic" Scheme – d-apply (define (d-apply procedure arguments calling-env) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments calling-env))) ;CHANGE: use calling env (else (error "Unknown procedure" procedure))))

9 8 Implement lazy evaluation Beyond Scheme – designing language variants: Lazy evaluation Complete conversion – normal order evaluator Upward compatible extension – lazy, lazy-memo

10 9 Normal Order (Lazy) Evaluation Alternative models for computation: Applicative Order: evaluate all arguments, then apply operator Normal Order: go ahead and apply operator with unevaluated argument subexpressions evaluate a subexpression only when value is needed to print by primitive procedure

11 10 Limitations of Applicative Order Evaluation Suppose we wanted to create: (define (unless condition usual-value exceptional-value) (if condition exceptional-value usual-value)) Which can be used in expressions such as: (unless (= b 0) (/ a b) (begin (display "exception: returning 0") 0)) This won't work in an applicative-order language! But will work with normal –order.

12 11 Strict vs. Non-Strict Arguments If the body of a procedure is evaluated before an argument has been evaluated we say that the procedure is non-strict in that argument. If the argument is evaluated before the body of the procedure is entered we say that the procedure is strict in that argument. Applicative order: strict in all arguments. Normal order: strict in arguments only in primitive procedures.

13 12 How to Implement Lazy Evaluation (define (foo x y)(+ x y)) GE p: x y body:(+ x y) foo: E1 x: “(+ 1 2)” y: “(+ 3 4)” (foo (+ 1 2) (+ 3 4)) Eval will not evaluate the parameters passed to apply! How are these delayed parameters passed? Via the binding in the extended environment E1!

14 13 How can we implement lazy evaluation? (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))))

15 14 Lazy Evaluation – l-eval Most of the work is in l-apply ; need to call it with: actual value for the operator just expressions for the operands the environment... (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))))

16 15 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))))

17 16 Representing Thunks Abstractly – a thunk is a "promise" to return a value when later needed ("forced") Concretely – our representation: thunk envexp

18 17 Thunks – delay-it and force-it (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 (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)))

19 18 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)

20 19 (l-eval ‘((lambda (x) (+ x x) ) (+ 1 1)) GE) (l-apply (actual-value ‘(lambda (x) (+ x x)) GE) ‘((+1 1 )) GE) (force-it (l-eval ‘(lambda (x) (+ x x)) GE)) ‘(procedure ‘(x) ‘((+ x x)) GE) (l-apply ‘(procedure ‘(x) ‘(+ x x) GE) ‘((+1 1 )) GE) (l-eval ‘(+ x x) E1) x : (‘thunk ‘(+1 1) GE) GE E1 (l-apply (actual-value ‘+ E1) ‘(x x) E1) (l-apply ‘(primitive #[add]) ‘(x x) E1) (apply-primitive-procedure ‘(primitive #[add]) (actual-value x E1)(actual-value x E1) ) (force-it (l-eval x E1)) (force-it (‘thunk ‘(+ 1 1) GE)) (actual-value ‘(+ 1 1) GE) (force-it (l-eval ‘(+ 1 1) GE)) ==> 2

21 20 (l-eval ‘(define f (lambda (x y) x)) GE) (eval-definition ‘(define f (lambda (x y) x)) GE) (define-variable! ‘f (l-eval ‘(lambda (x y) x) GE) GE) f: ‘(procedure ‘(x y) ‘(x) GE) GE

22 21 (l-eval ‘(f 1 1) GE) (l-apply (actual-value ‘f GE) ‘(1 1) GE) (force-it (l-eval ‘f GE)) ==> ‘(procedure ‘(x y) ‘(x) GE) (l-apply ‘(procedure ‘(x y) ‘(x) GE) ‘(1 1) GE) (l-eval ‘x E1) x : (‘thunk 1 GE) y: (‘thunk 1 GE) GE E1 ==> (‘thunk 1 GE)

23 22 (l-eval ‘(f (f 1 1) 1) GE) (l-apply (actual-value ‘f GE) ‘((f 1 1) 1) GE) (force-it (l-eval ‘f GE)) ‘(procedure ‘(x y) ‘(x) GE) (l-apply ‘(procedure ‘(x y) ‘(x) GE) ‘((f 1 1) 1) GE) (l-eval ‘x E1) x : (‘thunk ‘(f 1 1) GE) y: (‘thunk 1 GE) GE E1 ==> (‘thunk ‘(f 1 1) GE) Lets force this thunk..

24 23 (force-it (‘thunk ‘(f 1 1) GE)) (actual-value ‘(f 1 1) GE) (force-it (l-eval ‘(f 1 1) GE)) (force-it (l-apply (actual-value ‘f GE) ‘(1 1) GE)) (force-it (‘thunk 1 GE)) (actual-value 1 GE) (force-it (l-eval 1 GE)) ==> 1

25 24 Memo-izing Thunks Idea: once thunk exp has been evaluated, remember it If value is needed again, just return it rather than recompute thunk envexp Concretely – mutate a thunk into an evaluated-thunk evaluated- thunk result

26 25 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)))

27 26 Laziness and Language Design We have a dilemma with lazy evaluation Advantage: only do work when value actually needed Disadvantages –not sure when expression will be evaluated; can be very big issue in a language with side effects –may evaluate same expression more than once Alternative approach: give programmer control! Memoization doesn't fully resolve our dilemma Advantage: Evaluate expression at most once Disadvantage: What if we want evaluation on each use?

28 27 Variable Declarations: lazy and lazy-memo We want to extend the language as follows: (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.

29 28 Controllably Memo-izing Thunks thunk – never gets memoized thunk-memo – first eval is remembered evaluated-thunk – memoized-thunk that has already been evaluated when forced Thunk Memo envexp evaluated- thunk result

30 29 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))))

31 30 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)))

32 31 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))))

33 32 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))))

34 33 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))))

35 34 Summary Lazy evaluation – control over evaluation models Convert entire language to normal order Upward compatible extension –lazy & lazy-memo parameter declarations

36 35 How do we use this new lazy evaluation? Our users could implement a stream abstraction: (define (cons-stream x (y lazy-memo)) (lambda (msg) (cond ((eq? msg 'stream-car) x) ((eq? msg 'stream-cdr) y) (else (error "unknown stream msg" msg))))) (define (stream-car s) (s 'stream-car)) (define (stream-cdr s) (s 'stream-cdr))

37 36 Go over this example at home: infinite streams We 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:

38 37 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…

39 38 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) 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.

40 39 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))

41 40 Eval again, and it’s … a procedure (force-it (define-variable! ‘ones (l-apply (actual-value ‘cons GE) ‘(1 ones) 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))

42 41 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))

43 42 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))

44 43 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))

45 44 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))

46 45 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. GE E1 x: (list 'thunk ‘1 GE) y: (list ‘thunk ‘ones GE)) ones: p: m body: (m x y)

47 46 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)))

48 47 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.

49 48 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)))

50 49 (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.

51 50 Back to L-eval (force-it (l-eval (l-apply ones ‘(lambda (p q) p) (extend-environment …))) We move the ball to l-apply…

52 51 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)))))) E2

53 52 And l-eval again… L-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)

54 53 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)

55 54 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!

56 55 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))


Download ppt "1 Lecture 19 Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)"

Similar presentations


Ads by Google