220x Filetype PDF File size 0.19 MB Source: people.eecs.berkeley.edu
6.001 SICP Normal Order (Lazy) Evaluation Alternative models for computation: Applicative Order: Normal (Lazy) Order Evaluation evaluate all arguments, then apply operator Memoization Streams Normal Order: pass unevaluated expression to function evaluate only when needed for primitive operation 6.001 SICP 1 6.001 SICP 2 Applicative Order vs. Normal (Lazy) Order Applicative vs. Normal? (define (foo x) (write-line "inside foo") (define (try a b) (+ x x)) (if (= a 0) 1 b)) (foo (begin (write-line "eval arg") 222)) (try 0 (/ 1 0)) (try 0 (pi-to-this-many-digits 10000000)) Applicative Order: Normal (Lazy) Order: eval arg inside foo inside foo eval arg => 444 eval arg We first evaluated argument, => 444 then substituted value into the As if we substituted the body of the procedure unevaluated expression in the 6.001 SICP body of the procedure 3 6.001 SICP 4 Exercise – Problem with Applicative Order How can we implement lazy evaluation? Why cant we use define to define any special form? (define (l-apply procedure arguments env) ; changed (cond ((primitive-procedure? procedure) E.g write a procedure safe/ that only divide if b <> 0 (apply-primitive-procedure procedure (list-of-arg-values arguments env))) (define (unless pred usual exc) ((compound-procedure? procedure) (if pred exc usual)) (l-eval-sequence (define (safe/ a b) (procedure-body procedure) (unless (= b 0) (/ a b) 0) (extend-environment (procedure-parameters procedure) (define (safe/ a b) (list-of-delayed-args arguments env) (cond ((= b 0) 0) (procedure-environment procedure)))) (else (/ a b)))) (else (error "Unknown proc" procedure)))) 6.001 SICP 5 6.001 SICP 6 1 Thunks–a delayed argument Thunks–delay-it and force-it Abstractly –a thunk is a "promise" to return a value when (define (delay-it exp env) (list 'thunk exp env)) later needed ("forced") (define (thunk? obj) (tagged-list? obj 'thunk)) (define (thunk-exp thunk) (cadr thunk)) Concretely –our (define (thunk-env thunk) (caddr thunk)) representation: (define (force-it obj) thunk exp env (cond ((thunk? obj) (actual-value (thunk-exp obj) (else obj))) (thunk-env obj))) (delay-it exp env) –promise to evalexp later (define (actual-value exp env) (force-it exp) –evalexp now, leave no thunks (force-it (l-eval exp env))) 6.001 SICP 7 6.001 SICP 8 Exercise – Applicative vers Normal Order Memo-izing evaluation Write a function normal-order? That returns #t if the In lazy evaluation an arg is reevaluate each time it is used language is normal order and #f if the language is In applicative order evaluation argument is evaluated once applicative order. Can we keep track of values once we’ve obtained them, (define (normal-order?) and avoid cost of reevaluation? (let ((x #t)) ((lambda (x) ’()) (begin (set! x #f) ’())) x)) thunk exp env evaluated- result 6.001 SICP thunk 6.001 SICP 9 10 Thunks–MemoizingImplementation Laziness and Language Design (define (evaluated-thunk? obj) We have a dilemma with lazy evaluation (tagged-list? obj 'evaluated-thunk)) Advantage: only do work when value actually needed (define (thunk-value evaluated-thunk) Disadvantages (cadr evaluated-thunk)) –not sure when expression will be evaluated; can be (define (force-it obj) very big issue in a language with side effects (cond ((thunk? obj) –may evaluate same expression more than once (let ((result (actual-value (thunk-exp obj) (thunk-env obj)))) Memoization doesn't fully resolve our dilemma (set-car! obj 'evaluated-thunk) Advantage: Evaluate expression at most once (set-car! (cdr obj) result) Disadvantage: What if we want evaluation on each use? (set-cdr! (cdr obj) '()) result)) ((evaluated-thunk? obj) (thunk-value obj)) (else obj))) Alternative approach: give programmer control! 6.001 SICP 11 6.001 SICP 12 2 Variable Declarations: lazy and lazy-memo Syntax Extensions – Parameter Declarations Handle lazy and lazy-memo extensions in an upward- (define (first-variable var-decls) (car var-decls)) compatible fashion.; (define (rest-variables var-decls) (cdr var-decls)) (lambda (a (b lazy) c (d lazy-memo)) ...) (define declaration? pair?) "a", "c" are the usual kind of variables (evaluated before (define (parameter-name var-decl) procedure application (if (pair? var-decl) (car var-decl) var-decl)) "b" is lazy, “Normal Order”: it gets (re)-evaluated each (define (lazy? var-decl) time its value is actually needed (and (pair? var-decl) (eq? 'lazy (cadr var-decl)))) "d" is lazy-memo; it gets evaluated the first time its (define (memo? var-decl) value is needed, and then that value is returned again (and (pair? var-decl) any other time it is needed again. (eq? 'lazy-memo (cadr var-decl)))) 6.001 SICP 13 6.001 SICP 14 Controllably Memo-izing Thunks A new version of delay-it thunk –nevergets memoized Look at the variable declaration to do the right thing... thunk-memo –first eval is remembered (define (delay-it decl exp env) evaluated-thunk –memoized-thunk that has (cond ((not (declaration? decl)) already been evaluated (l-eval exp env)) ((lazy? decl) (list 'thunk exp env)) when thunk- exp env ((memo? decl) forced memo (list 'thunk-memo exp env)) (else (error "unknown declaration:" decl)))) evaluated- result thunk 6.001 SICP 15 6.001 SICP 16 Change to force-it Order Comparison (define (force-it obj) (define (foo x) (cond ((thunk? obj) ;eval, but don't remember it (write-line "inside foo") (actual-value (thunk-exp obj) (+ x x)) (thunk-env obj))) ((memoized-thunk? obj) ;eval and remember (foo (begin (write-line "eval arg") 222)) (let ((result (actual-value (thunk-exp obj) Applicative Normal (lazy) MemoizedNormal (thunk-env obj)))) Order: Order: (Lazy-memo) Order: (set-car! obj 'evaluated-thunk) (set-car! (cdr obj) result) eval arg inside foo inside foo (set-cdr! (cdr obj) '()) inside foo eval arg eval arg result)) eval arg => 444 ((evaluated-thunk? obj) (thunk-value obj)) => 444 => 444 (else obj))) 6.001 SICP 17 6.001 SICP 18 3 Exercise Stream Object Given this definition of l-abs: A pair-like object, except the cdr part is lazy (not evaluated until needed): (define (l-abs (i lazy)) (if (< i 0) (- i) i)) cons-stream Trace the following use of l-abs: stream-cdr stream-car (define (down) (begin (set! x (- x 2)) x) a a (define x 3) value thunk-memo (l-abs (down)) (define (cons-stream x (y lazy-memo)) (cons x y)) (define stream-car car) (define stream-cdr cdr) 6.001 SICP 19 6.001 SICP 20 What will these print? (ex1) Decoupling computation from description (define s (cons 5 (begin (write-line 7) 9))) Can separate order of events in computer from apparent (car s) order of events in procedure description (cdr s) (list-ref (filter (lambda (x) (prime? x)) (define t (cons-stream 5 (begin (write-line 7) 9))) 100) (enumerate-interval 1 100000000)) (stream-car t) (stream-ref (stream-filter (lambda (x) (prime? x)) (stream-cdr t) 100) (stream-interval 1 100000000)) 6.001 SICP 21 6.001 SICP 22 Decoupling computation from description Decoupling computation from description (define (stream-interval a b) (define seq (stream-interval 1 10)) (if (> a b) (define y (stream-filter even? seq)) the-empty-stream (cons-stream a (stream-interval (+ a 1) b)))) Now! Lets do some calculation... (define (filter-stream pred str) (stream-ref y 3) -> 8 (if (pred (stream-car str)) (cons-stream (stream-car str) (filter-stream pred (stream-cdr str))) (filter-stream pred (stream-cdr str)))) 6.001 SICP 23 6.001 SICP 24 4
no reviews yet
Please Login to review.