<<

Where are we? We've added S-expressions (and booleans) to Impcore CS301 We've seen how to use them to implement sets Session 5 dictionaries We've introduced lambda and the idea of first- class functions

1 2

Where are we going? Lambda A look at how functions can be treated as values Creates unnamed function A long look at how to exploit first-class functions (lambda (x) (* x 3)) ... the function that multiplies its argument by 3 In (lambda (x) (+ x y)) ... x is bound, but y is free

3 4 Uses of lambda Nested functions for Define nested functions using letrec mutual recursion Pass functions as parameters From problem set 2: (define preord (t) Return functions as results (letrec ((pre* (lambda (ts) Store them in data structures (if (null? ts '() (append (preord (car ts)) (pre* (cdr ts)))))) ) (if (leaf? t) (list1 t) (cons (label t) (pre* (cdr t))))))

5 6

Nested functions Implementing nesting

Free variables Easy: keep stack of "displays" at run time (define contig-sublist? (l1 l2) not the call stack! (letrec location of free variable known at compile time ((prefix (lambda (f s) (if (null? f) #t Familiar "static scoping" rule (if (null? s) #f (if (equal? (car f) (car s)) (prefix (cdr f) (cdr s)) (prefix l1 (cdr s)))))))) (prefix l1 l2))) Free variable l1 used to "reset" the search!

7 8 Functions as arguments Implementation (define twice (f x) (f (f x))) Still straightforward: n and m are on the call stack while the lambda is being called in twice (define nsq*m(n m) At runtime, maintain links backward in the call (twice (lambda (x) (* n x)) stack to find the values of free variables m)) -> (nsq*m 3 2) 18

What are the bound and free variables?

9 10

Lambda: the great escape Example

Things are different when we return functions as -> (define mult-by (n) (lambda (x) (* n x))) results or store them in data structures! mult-by -> ((mult-by 3) 4) The free variables "escape" their original 12 environment Question: after (mult-by 3)returns, what does n mean? Now we need closures: in our interpreters a In a naive implementation the parameter context is gone! lambda evaluates to a pair containing the code ...thus closures! and the current environment

11 12 Application: Function Application: composition -> (define put-head (x) ((curry cons) x)) put-head (define o (f g) -> (val put-a (put-head 'a)) (lambda (x) (f (g x)))) -> (put-a '(b d)) -> (val caddr (o car (o cdr cdr))) (a b c d) -> (put-a '(b l e)) -> (caddr '(a b c d)) (a b l e) c

13 14

"own variables" H-O functions in the

(define -box (key open) (lambda (k) standard basis (if (equal? k key) (if open Besides compose and curry... (begin (set open #f) '(you have locked the box)) (begin (set open #t) '(you have unlocked the box))) '(that is not the right key)))) Applying predicates to lists via filter, exists?, all? -> (val box1 (lock-box 42 #f)) -> (val box2 (lock-box 7 #t)) Transforming lists via map -> (box1 42) (you have unlocked the box) General list catamorphisms foldl and foldr -> (box1 42) (you have locked the box) -> (box2 42) (that is not the right key) -> (box2 7) (you have locked the box -> (box2 7) (you have unlocked the box)

15 16 Predicates The list transformer map

Predicate: a function that returns a boolean Apply a function of one argument to each element (val pos ((curry <) 0)) -> (map pos '(-3 3 -4 4 -1 0 5)) -> (filter pos '(-3 3 -4 4 -1 0 5)) (#f #t #f #t #f #f #t) (3 4 5) -> (map Let's implement filter on the board (lambda (x) (cons x '())) '(a b c)) ((a) (b) (c))

17 18

Fold operators Using fold

As a result of (define countall (x xs) (foldl (foldr f z '(1 2 3)) (lambda (y sum) (+ sum (if (equal? x y) ... the input 1 (if (pair? y) (cons 1 (cons 2 (cons 3 '()))) (countall x y) ... is transformed into the value of 0)))) 0 xs)) (f 1 (f 2 (f 3 z )))

19 20 Using fold Folding from the left

As a result of (define preord (t) (if (leaf? t) (foldl f z '(1 2 3)) (list1 t) (cons (label t) ... the input (foldr (lambda (t l) (cons 1 (cons 2 (cons 3 '()))) (append (preord t) l)) ... is transformed into the value of '() (f 3 (f 2 (f 1 z ))) (cdr t)))))

21 22

!"#$# versus !"#$% Next time -> (foldr cons '() '(1 2 3)) Using higher-order functions (1 2 3) polymorphism continuations -> (foldl cons '() '(1 2 3)) (3 2 1)

23 24 Where are we? We've seen how Scheme's first-class functions can be used for local function definitions CS301 to pass functions as parameters Session 6 to return functions as results We've looked at the standard basis functions o curry all? exists? filter map foldl foldr

1 2

Where are we going? Polymorphism - the Higher-order functions for polymorphism problem Higher-order functions and continuation-passing Sets, yes, but sets of what? style -> (val emptyset ’()) -> (define member? (x s) (exists? ((curry equal?) x) s)) -> (define add-element (x s) (if (member? x s) s (cons x s))) -> (define union (s1 s2) (foldl add-element s1 s2)) -> (define set-from-list (l) (foldl add-element ’() l)) -> (union ’(1 2 3 4) ’(2 4 6 8)) (8 6 1 2 3 4)

3 4 Wanted: generality Equality for a-lists

What if we want sets of a-lists? The right test: The predicate equal? isn't the right thing! (define sub-alist? (d1 d2) (all? (equal? '((U Thant)(I Ching)(E coli)) (lambda (pair) '((E coli)(I Ching)(U Thant))) (equal? (cadr pair) (find (car pair) d2))) d1)) #f (define =alist? (d1 d2) ...but that's what's used in member? (if (sub-alist? d1 d2) (sub-alist? d2 d1) #f))

5 6

Clunky polymorphism Critique

Redefine set ops to expect equality test as parameter We have to pass the equality predicate around wherever we use the set - wordy, awkward, and (define member? (x s eqfun) (exists? ((curry eqfun) x) s)) error-prone (define add-element (x s eqfun) Better idea: make the predicate part of the set (if (member? x s eqfun) s (cons x s))) ... But then we have to redefine all the set ops! And to use: (member x s =alist) (member y s' equal?)

7 8 Polymorphism version 2 Critique

Represent set as pair of function and data (like a Now we can use the set without explicitly simple object in an OO language) mentioning the equality predicate - good! (define mk-set (eqfun elements) And there's no danger of using the wrong predicate (cons eqfun elements)) (define eqfun-of (set) (car set)) - good! (define elements-of (set) (cdr set)) But every set we have contains an extra cons cell - (val emptyset (lambda (eqfun) (mk-set eqfun ’()))) (define member? (x s) even when there are many sets and only a few (exists? ((curry (eqfun-of s)) x) (elements-of s))) (define add-element (x s) different equality predicates - which might be bad! (if (member? x s) s (mk-set (eqfun-of s) (cons x (elements-of s))))) To avoid the cons cell, package the set ops as a closure over the predicate

9 10

Polymorphism version 3 Critique (val mk-set-ops We got rid of the cons cell - good! (lambda (eqfun) (list2 But we might use the wrong set op for our set - ! (lambda (x s) bad! (exists? ((curry eqfun) x) s)) ! (lambda (x s) And we have to define named functions for each ! (if (exists? ((curry eqfun) x) s) s type of set - which we might not like. ! (cons x s)))))) (val al-nullset ’()) Later we'll study language features to give us real (val list-of-al-ops (mk-set-ops =alist?)) (val al-member? (car list-of-al-ops)) polymorphism directly. (val al-add-element (cadr list-of-al-ops))

11 12 Continuation passing Continuations for errors

Scheme's major innovation: the notion of A-list find confuses unbound keys with keys continuation bound to '() Full Scheme has a built-in construct for Clunky solution: special return values continuation handling: call/cc Elegant solution: client of find passes two The granddaddy of throwing/catching exceptions functions: a success continuation and a failure In uScheme we will program this explicitly with continuation higher-order functions

13 14

find with continuations Backtracking (define find-c (key alist success failure) We can use continuations to implement a (letrec backtracking search ((search (lambda (alist) ! (if (null? alist) ! (failure) ! (if (equal? key (caar alist)) ! ! (success (cadar alist)) ! ! (search (cdr alist))))))) (search alist)))

(define find-default (key table default) (find-c key table (lambda (x) x) ! (lambda() default)))

15 16 The SAT problem A basic search module

NP-complete, but lots of practical apps succeed, fail, and resume are continuations start succeed Problem: find an assignment of booleans to variables that satisfies a CNF formula fail resume Example formula: (x y z) (w y) (w z) ∨ ∨ ¬ ∧ ∨ ∧ ∨ succeed takes a resume continuation to allow Structure: conjuncts, disjuncts, literals backtracking in case of failure in later module Some satisfying assignments: Instantiate the module for each node of the search x T,y F,z T,w T tree { !→ !→ !→ !→ } x F,y T,z T,w F { !→ !→ !→ !→ }

17 18

Backtracking for SAT The disjunction solver

To solve a conjunction: Now an empty list is UNsatisfiable!

(define solve-conjunction (conjuncts cur fail succeed) (define solve-disjunct (disjuncts cur fail succeed) (if (null? conjuncts) (if (null? disjuncts) (succeed cur fail) (fail) (solve-disjunction (car conjuncts) cur fail (solve-literal (car disjuncts) cur (lambda (cur resume) (lambda () (solve-conjunction (cdr conjuncts) (solve-disjunct (cdr disjuncts) cur fail cur resume succeed))))) succeed)) On backtrack, fail succeed))) One satisfiable literal is all we need If the first conjunct is solved, continue with the rest of the conjuncts On backtrack, look at the rest of the disjuncts

19 20 Solving a literal Watch the backtracking:

-> (one-solution f1) Bind if possible/needed (conjunction: ((x y) ((not x)) (y z)) soln: ()) (disjunction: (x y) soln: ()) (define solve-literal (lit cur fail succeed) success (if (satisfies? lit cur) (conjunction: (((not x)) (y z)) soln: ((x #t))) (succeed cur fail) ;fail on backtrack (disjunction: ((not x)) soln: ((x #t))) (if (binds? lit cur) fail (fail) (disjunction: () soln: ((x #t))) (succeed (bind (variable-of lit) fail (disjunction: (y) soln: ()) (satisfying-value lit)) cur) success fail)))) ;fail on backtrack (conjunction: (((not x)) (y z)) soln: ((y #t))) (disjunction: ((not x)) soln: ((y #t))) No backtracking possible at this level, so we pass success (conjunction: ((y z)) soln: ((y #t) (x #f))) the failure continuation as the "resume" (disjunction: (y z) soln: ((y #t) (x #f))) success continuation (conjunction: () soln: ((y #t) (x #f))) ((y #t) (x #f))

21 22

So what? What next? Treating functions as first-class values is a simple An interpreter for uScheme written in ML idea with far-reaching consequences ... so next week's mission is to acquire a Programs can create functions on the fly, rudimentary knowledge of ML capturing the current environment in closures When you have a language with this feature, use functions freely for control structure, data encapsulation, and whatever else you might dream up

23 24