MoDvaDon for local bindings We want local bindings = a way to name things locally in Local Bindings and Scope funcDons and other expressions. Why? These slides borrow heavily from Ben Wood’s Fall ‘15 slides, some of which are – For style and convenience in turn based on Dan Grossman’s material from the University of Washington. – Avoiding duplicate computaDons – A big but natural idea: nested funcDon bindings CS251 Programming – Improving algorithmic efficiency ( not “just a liQle faster”) Languages Fall 2018, Lyn Turbak Department of Computer Science Wellesley College Local Bindings & Scope 2 let in the let expressions: Example quadraDc formula > ( let {[a (+ 1 2)] [b (* 3 4)]} (list a b)) (define (quadratic-roots a b c) '(3 12) (let {[-b (- b)] [sqrt-discriminant Pre$y printed form (sqrt (- (* b b) (* 4 a c)))] [2a (* 2 a)]} > ( let {[a (+ 1 2)] (list (/ (+ -b sqrt-discriminant) 2a) [b (* 3 4)]} (/ (- -b sqrt-discriminant) 2a)))) (list a b)) '(3 12) Local Bindings & Scope 3 Local Bindings & Scope 4
Parens vs. Braces vs. Brackets Formalizing let expressions As matched pairs, they are interchangeable. Differences can be used to enhance readability. 2 quesDons: a new keyword! > (let {[ a (+ 1 2) ] [ b (* 3 4) ]} (list a b)) • Syntax: (let {[ Id1 E1 ] ... [ Idn En ]} Ebody ) '(3 12) – Each Idi is any identiier, and Ebody and each Ei are any expressions > (let (( a (+ 1 2) ) ( b (* 3 4) )) (list a b)) '(3 12) • Evaluation: > (let [[ a (+ 1 2) ] [ b (* 3 4) ]] (list a b)) – Evaluate each expression Ei to value Vi in the current dynamic environment. '(3 12) – Evaluate Ebody[V1,…Vn/Id1,…,Idn] in the current > (let [{ a (+ 1 2) } ( b (* 3 4) )] (list a b)) dynamic environment. '(3 12) Result of whole let expression is result of evaluating Ebody . Local Bindings & Scope 5 Local Bindings & Scope 6 let is an expression let is just syntactic sugar! A let-expression is just an expression , so we can use it ( let {[ Id1 E1 ] … [ Idn En ]} Ebody ) anywhere an expression can go. Silly example: desugars to (( lambda ( Id1 … Idn ) Ebody ) E1 … En ) (+ (let {[x 1]} x) (let {[y 2] Example: [z 4]} (- z y))) (let {[ a (+ 1 2) ] [ b (* 3 4) ]} (list a b)) desugars to ((lambda (a b) (list a b)) (+ 1 2) (* 3 4)) Local Bindings & Scope 7 Local Bindings & Scope 8
( if (> (first xs) Avoid repeated recursion Fast vs. unusable ( bad-maxlist (rest xs))) (first xs) ( bad-maxlist (rest xs))) Consider this code and the recursive calls it makes (bad-maxlist (range 50 0 -1)) – Don’t worry about calls to first , rest , and null? because they do a small constant amount of work bm 50,… bm 49,… bm 48,… bm 1 (bad-maxlist (range 1 51)) (define ( bad-maxlist xs) ( if (null? xs) bm 1,… bm 2,… bm 3,… bm 50 -inf.0 ( if (> (first xs) ( bad-maxlist (rest xs))) bm 3,… (first xs) 2 50 … ( bad-maxlist (rest xs))))) times bm 2,… bm 3,… bm 3,… bm 50 Local Bindings & Scope 9 Local Bindings & Scope 10 Some calculaDons Efficient maxlist Suppose one bad-maxlist call’s if logic and calls to null? , first? , rest take 10 -7 seconds total (define ( good-maxlist xs) – Then ( bad-maxlist (list 50 49 … 1)) takes 50 x 10 -7 sec ( if (null? xs) – And ( bad-maxlist (list 1 2 … 50)) -inf.0 takes (1 + 2 + 2 2 + 2 3 + … + 2 49 ) x 10 -7 ( let {[rest-max ( good-maxlist (rest xs))]} = (2 50 - 1) x 10 -7 = 1.12 x 10 8 sec = over 3.5 years ( if (> (first xs) rest-max) – And ( bad-maxlist (list 1 2 … 55)) (first xs) takes over 114 years rest-max)))) – And ( bad-maxlist (list 1 2 … 100)) takes over 4 x 10 15 years . (Our sun is predicted to die in about 5 x 10 9 years) – Buying a faster computer won’t help much J gm 50,… gm 49,… gm 48,… gm 1 The key is not to do repeated work! gm 1,… gm 2,… gm 3,… gm 50 – Saving recursive results in local bindings is essential … Local Bindings & Scope 11 Local Bindings & Scope 12
Transforming good-maxlist Your turn: sumProdList (define ( good-maxlist xs) ( if (null? xs) Given a list of numbers, sumProdList returns a pair of -inf.0 (1) the sum of the numbers in the list and ( let {[rest-max ( good-maxlist (rest xs))]} ( if (> (first xs) rest-max) (2) The product of the numbers in the list (first xs) rest-max)))) (sumProdList '(5 2 4 3)) –> (14 . 120) (define ( good-maxlist xs) (sumProdList '()) –> (0 . 1) ( if (null? xs) -inf.0 Define sumProdList . Why is it a good idea to use let (( � (fst rest-max) ; name fst too! in your definiDon? ( if (> fst rest-max) fst rest-max)) (first xs) ( good-maxlist (rest xs))))) (define ( good-maxlist xs) (define ( max a b) ( if (null? xs) ( if (> a b) a b)) -inf.0 ( max (first xs) ( good-maxlist (rest xs))))) Local Bindings & Scope 13 Local Bindings & Scope 14 Scope and Lexical Contours and and or sugar scope = area of program where declared name can be used. (and) desugars to #t Show scope in Racket via lexical contours in scope diagrams . (and E1 ) desugars to E1 (and E1 …) desugars to (if E1 (and …) #f) (define add-n ( λ ( x ) (+ n x )) ) (or) desugars to #f (define add-2n ( λ ( y ) (add-n (add-n y )))) (or E1 ) desugars to E1 (or E1 …) desugars to (define n 17) (let (( Id1 E1 )) (define f ( λ ( z ) (if Id1 Id1 (or …)) where Id1 must be fresh – i.e., not used elsewhere in (let {[ c (add-2n z ) ] the program. [ d (- z 3) ]} • Why is let needed in or desugaring but not and ? (+ z (* c d ))) ) ) • Why must Id1 be fresh? Local Bindings & Scope 15 Local Bindings & Scope 16
DeclaraDons vs. References Scope and Define Sugar A declara7on introduces an idenDfier (variable) into a scope. (define (add-n x ) (+ n x ) ) A reference is a use of an idenDfier (variable) within a scope. (define (add-2n y ) (add-n (add-n y )) ) We can box declaraDons, circle references, and draw a line (define n 17) from each reference to its declaraDon. Dr. Racket does this (define (f z ) for us (except it puts ovals around both declaraDons and references). (let {[ c (add-2n z ) ] An idenDfier (variable) reference is unbound if there is no [ d (- z 3) ]} declaraDon to which it refers. (+ z (* c d ))) ) ) Local Bindings & Scope 17 Local Bindings & Scope 18 Shadowing Alpha-renaming An inner declaraDon of a name shadows uses of outer declaraDons Can consistently rename idenDfiers as long as it doesn’t change the of the same name. “wiring diagram” between uses and declaraDons. (define (f w z) (define (f c d) (let {[x 2]} OK (* w (* c (let {[c (add-2n z)] (let {[b (add-2n d)] (- (let {[x (* x x)]} [d (- z 3)]} [c (- d 3)]} Can’t refer to (+ x 3)) (+ z (* c d)))))) (+ d (* b c)))))) outer x here. Not OK x )) (define (f x y) (* x (let {[x (add-2n y)] [y (- y 3)]} (+ y (* x y)))))) Local Bindings & Scope 19 Local Bindings & Scope 20
Scope, Free Variables, and Higher-order FuncDons Compare the Values of the Following In a lexical contour, an idenDfier is a free variable if it is not (let {[a (+ 2 3)] [b (* 3 4)]} defined by a declaraDon within that contour. (list a Scope diagrams are especially helpful for understanding the (let {[a (- b a)] meaning of free variables in higher order funcDons. [b (* a a)]} (list a b)) b)) (define (make-sub n ) ( λ ( x ) (- x n )) ) (let {[a (+ 2 3)] [b (* 3 4)]} (list a (let {[a (- b a)]} (let {[b (* a a)]} (define (map-scale factor ns ) (list a b))) b)) (map ( λ ( num ) (* factor num )) ns) ) Local Bindings & Scope 21 Local Bindings & Scope 22 More sugar: let* Local funcDon bindings with let (let* {} Ebody ) desugars to Ebody • Silly example: (let* {[ Id1 E1 ] …} Ebody ) (define (quad x) (let ([square (lambda (x) (* x x))]) desugars to (let {[ Id1 E1 ]} (square (square x)))) (let* {…} Ebody )) • Private helper functions bound locally = good style. Example (same as 2 nd example on previous slide) • But can’t use let for local recursion. Why not? (let {[a (+ 2 3)] [b (* 3 4)]} ? (define (up-to-broken x) (list a (let {[between (lambda (from to) (let* {[a (- b a)] (if (> from to) [b (* a a)]} null (cons from (list a b)) (between (+ from 1) to))))]} b)) (between 1 x))) Local Bindings & Scope 23 Local Bindings & Scope 24
Recommend
More recommend