VIEWS: 15 PAGES: 28 POSTED ON: 3/16/2011 Public Domain
Programming Languages COMP 321 Spring 2008 Practicum 5 Practicum 5 • lists as trees • tail recursion • let and letrec reference • reference: – Dybvig chapter 2, sections 5-7 recursion review • finding the length of a list (define length (lambda (ls) (define list-copy (lambda (ls) (if (null? ls) (if (null? ls) 0 ‘( ) (+ (length (cdr ls)) 1) (cons (car ls) ) (list-copy (cdr ls))) ) ) ) ) ) Deep Recursion review finding the number of items in a list (that contains lists) example: (1 2 (3 4) 5 (6 (7 8) 9) 10) contains 10 items. Note that pair? Returns false if a list is empty. (define numItems (lambda (ls) (if (null? ls) 0 (if (or (null? (car ls)) (pair? (car ls)) (+ (numItems (car ls)) (numItems (cdr ls))) (+ (numItems (cdr ls)) 1) ) ) ) ) Lists as trees • Can represent a list as a tree: (a (b c d) ((e f) g)) a (b c d) ((e f) g) b c d (e f) g e f recursion • tree-copy treats the structure of pairs as a tree rather than as a list – left subtree is car, right subtree is the cdr – builds a new tree, leaves the leaves alone (define tree-copy (lambda (tr) (if (not (pair? tr)) (tree-copy ‘((a . b) . c)) tr (cons (tree-copy (car tr)) (tree-copy (cdr tr))) ) ) ) Lists as trees • procedure depth takes a list item as arg and returns depth of the tree (define depth (lambda (item) (if (not (pair? item)) 0 (max (add1 (depth (car item))) (depth (cdr item)))))) • max is a built-in procedure Lists as trees • procedure flatten takes a list ls as arg and returns a list of all the leaves (define flatten (lambda (ls) (cond ((null? ls) ‘()) ((pair? (car ls)) (append (flatten (car ls))(flatten (cdr ls)))) (else (cons (car ls)(flatten (cdr ls))))))) • append is a built-in procedure; takes two lists and combines into one list Lists as trees • flat recursion is over the top-level items of a list – equivalent to recursion over the nodes of the corresponding tree, where the nodes are one level below the root. • deep recursion is over all items of a list – equivalent to recursion over the leaves of the tree Tail recursion • Consider the factorial procedure below (define fact (lambda (n) (if (zero? n) 1 (* n (fact (sub1 n)))))) Tail recursion • when fact is applied to a number, like 3, a return table is built – must wait for the return value from the recursive call to finish this call – when reach base case, work backwards, filling in the table. Called backward substitution answer1 is (* 3 answer2) answer2 is (* 2 answer3) answer3 is (* 1 answer4) answer4 is 1 Tail recursion • another way of looking at the table (the recursive calls and the returns): (fact 3) (* 3 (fact 2)) (* 3 (* 2 (fact 1))) (* 3 (* 2 (* 1 (fact 0)))) (* 3 (* 2 (* 1 1))) (* 3 (* 2 1)) (* 3 2) 6 Tail recursion • to find the factorial of n, must perform n + 1 invocations and will have n + 1 rows in the return table. • for every call to fact must – create the return table – perform backward substitution • This is space and time expensive! • Better type of recursion: – change the solution so that you do not have to do any computation when the recursive call returns – then do not need either a return table or backward substitution – this is called an iterative process or tail-recursion Tail recursion • changing fact to a tail recursive procedure – need a helper function. – need another variable to accumulate the value of the result (define fact-it (lambda (n acc) (if (zero? n) acc (fact-it (sub1 n) (* acc n))))) (define fact (lambda (n) (fact-it n 1))) let expressions review • variables and let expressions (let ((x 1)) (let ((x (+ x 1))) (+ x x) ) ) (let ((x 1)) (let ((new-x (+ x 1))) (+ new-x new-x)) ) procedures in let • lambda expression in let (let ((double (lambda (x) (+ x x)))) (list (double (* 3 4)) (double (/ 99 11)) (double (- 2 7))) ) closure • Consider the procedure: (define addb (let ((b 100)) (lambda (x) (+ x b)))) • There are 3 parts to the lambda expression: – The list of prameters – The body of the lambda expression (+ x b) – The nonlocal environment in which the lambda was defined (the let expression in this case) bindings • Local bindings always take precedence over global or other nonlocal bindings: (define a 5) (add1 a) 6 (let ((a 3)) (add1 a)) 4 (add1 a) 6 (let ((a 5)) (display (add1 a)) (let ((a 3)) (display (add1 a))) (add1 a))) 6 4 6 bindings • In general, the let expression (let ((var1 val1) (var2 val2) … (varn valn)) body) • is equivalent to ((lambda (var1 var2 … varn) body) val1 val2 … valn) bindings • Local bindings always take precedence over global or other nonlocal bindings: (define a 10) (define b 2) (let ((a (+ a 5))) (* a b)) 30 ?? (let ((a 10)(b 2)) (let ((a (+ a 5))) (* a b))) 30 ?? closure • Local bindings always take precedence over global or other nonlocal bindings: (define addb (let ((b 100)) (lambda (x) (+ x b)))) (let ((b 10)) (addb 25)) 125 ??? closure • A closure includes the environment in which the procedure was defined: (let ((b 2)) b 2 (let ((add2 (lambda (x) (+ x b))) (b 0.5)) environment 1 (/ b (add2 b)))) (/ 0.5 2.5) or 0.2 ??? procedure (x) (+ x b) Env 1 b 0.5 environment 2 closure • the procedure is still defined within environment 1; add2 does not know about the second definition of b: b 2 (let ((b 2)) (let ((b 0.5) environment 1 (add2 (lambda (x) (+ x b)))) (/ b (add2 b)))) procedure (x) (+ x b) Env 1 (/ 0.5 ??? 2.5) or 0.2 b 0.5 environment 2 local functions • can define a local function (define getEven (lambda (ls) (let ( (evenNum (lambda (x) (zero? (remainder x 2)))) ) (if (null? ls) ‘() (if (evenNum (car ls)) (cons (car ls) (getEven (cdr ls))) (getEven(cdr ls)))) ))) local functions • Problem: local function cannot be recursive, since it does not know its own name (define fact (lambda (n) (let ( (fact-it (lambda (k acc) (if (zero? k) acc (fact-it (sub1 k) (* k acc))))) ) (fact-it n 1) ))) local functions • Solution: a special type of let called letrec (define fact (lambda (n) (letrec ( (fact-it (lambda (k acc) (if (zero? k) acc (fact-it (sub1 k) (* k acc))))) ) (fact-it n 1) ))) (test1 20) functions local(test2 20) 30 • Letrec functions 25 their local environment get (test3 20) from the letrec that’s being defined. 25 (define test1 (define test2 (define test3 (let ((x 10)) (let ((x 10)) (let ((x 10)) (lambda (y) (lambda (y) (lambda (y) (let ((x 5) (letrec ( (x 5) (letrec ((test4 (test2 (test3 (lambda (n) (lambda (n) (lambda (n) (+ n x))) (+ n x)))) (+ n x)))) (x 5) ) (test2 y))))) (test3 y))))) (test4 y)))))