CS 227X Lecture -*-Outline -*- * Data Driven Recursion (Ch. 4) ** Broad context (4.1) in depth study of recursion different varieties of recursion a way to derive the recursive step from examples relation of recursion to recursive definition of data type, so that you can program recursions for new types we don't teach ** Flat recursion (4.2) 1. Stopping Condition 2. treat car of list as a unit (1 step) 3. recursive steps are on cdr *** On lists (possibly empty) Want to define snoc, that is "cons backwards" ----------- snoc: (-> ((list T) T) (list T)) (snoc '() 'a) ==> (a) (snoc '(a b c) 'd) ==> (a b c d) ----------- - Several Plans: - reverse the list, use cons, reverse again (this is expensive, and to build reverse need something like snoc) - observe that it can be done by recursion ---------- (snoc '(a b c) d) = (a b c d) (snoc '(b c) d) = (b c d) (snoc '(c) d) = (c d) (snoc '() d) = (d) ---------- How do you form (a b c d) from a and (b c d) ? What is (b c d) in terms of snoc, '(a b c) and 'd ? A: (snoc (cdr '(a b c)) 'd) This is like subtraction ------------- Want: (snoc '(a b c) 'd) = '(a b c d) Given: (snoc (cdr '(a b c)) 'd) = '(b c d) (car '(a b c)) = 'a ========================================== (cons 'a (snoc (cdr '(a b c)) 'd)) = '(a b c d) ------------- i.e., (cons (car '(a b c)) .... ) is the difference. Now, can write snoc. ----------- (define snoc ; TYPE (-> ((list T) T) (list T)) (lambda (lst item) (if (null? lst) (cons item lst) (cons (car lst) (snoc (cdr lst) item))))) ----------- *** reverse Using snoc, can reverse a list ----------- reverse : (-> ((list T)) (list T)) (reverse '()) ==> () (reverse '(a b c)) ==> (c b a) ----------- The problem is to get (reverse '(a b c)) = (c b a) from (car '(a b c)) = a, (reverse (cdr '(a b c))) = (reverse '(b c)) = (c b) ---------------- Want: (reverse '(a b c)) = '(c b a) Given: (reverse (cdr '(a b c))) = '(c b) (car '(a b c)) = a ========================================== ----------------- Have them fill in the blank above and then write reverse in groups! For the blank: Have to put 'a on the end of '(c b) Use snoc ! in blank: (snoc (reverse (cdr ls)) (car ls) (define reverse (lambda (ls) (if (null? ls) '() (snoc (reverse (cdr ls)) (car ls))))) *** remove removes *all* of the given item from a list. ----------- remove : (-> (T (list T)) (list T)) (remove 'c '(a a b a c)) ==> (a a b a) (remove 'a '(a a b a c)) ==> (b c) (remove 'a '()) ==> () FILL THIS IN Want: (remove 'a '(a a b a c)) = '(b c) Given: ====================================== THEN WRITE REMOVE ----------- Have them do this in groups. 2 cases... 1. get (remove 'a '(a a b a c)) = '(b c) from (remove 'a (cdr '(a a b a c))) = '(b c) and (car '(a a b a c)) = 'a 2. get '(b c) = (remove 'a '(b a c)) from (remove 'a (cdr '(a c))) = '(c) and (car '(b a c)) = 'b Write this. Other examples of this pattern? *** Examples of flat recurion on non-empty lists ------------------ RECURSION ON NON-EMPTY LISTS Program the following without using reverse. prod: (-> ((list number)) number) (prod '(3)) ==> 3 (prod '(10 11)) ==> 110 (prod '(10 11 3)) ==> 330 (define prod (lambda (lst) ; REQUIRES: lst is not empty (if (null? (cdr lst)) (car lst) (* (car lst) (prod (cdr lst)))))) ------------------ *** summary ------------------------ 1. What is the difference between recursion over lists vs. non-empty lists? 2. Give a recursive definition of a. Lists b. Non-empty lists 3. How does each type of recursion relate to the definition of that type of data? ------------------------ My answers (don't have to say these): 1. stopping condition (null? ls) vs. (null? (cdr ls)) but everything else the same 2. a (list T) is either () or (cons x l), where x:T, l:(list T) a (non-empty-list T) is either (cons x '()) or (cons x l) where x:T, l:(non-empty-list T) 3. the base case of the definition determines the base case of the recursion, same for the recursive cases. Moral: it helps to analyze the type of arguments to determine the pattern of recursion to use. ** Simultaneous flat recursion ----------- SIMULTANEOUS FLAT RECURSION merge: (-> ((list number) (list number)) (list number)) (merge '() '(1 2 3)) ==> (1 2 3) (merge '(1 2 3) '()) ==> (1 2 3) (merge '(1 4 5) '(2 3 6)) ==> (1 2 3 4 5 6) (merge '(1 2 9) '(4 5 7)) ==> (1 2 4 5 6 7 9) (define merge (lambda (sorted-ntpl1 sorted-ntpl2) (cond ((null? sorted-ntpl1) sorted-ntpl2) ((null? sorted-ntpl2) sorted-ntpl1) ((< (car sorted-ntpl1) (car sorted-ntpl2)) (cons (car sorted-ntpl1) (merge (cdr sorted-ntpl1) sorted-ntpl2))) (else (cons (car sorted-ntpl2) (merge sorted-ntpl1 (cdr sorted-ntpl2)))) ))) ----------- counter-example ---------- append: (-> ((list T) (list T)) (list T)) (append '() '(a b c)) ==> (a b c) (append '(y) '(a b c)) ==> (x y a b c) (append '(x y) '(a b c)) ==> (x y a b c) (define append (lambda (ls1 ls2) (if (null? ls1) ls2 (cons (car ls1) (append (cdr ls1) ls2))))) ---------- this is NOT simultaneous recursion equal-lists would be like that. ** mutual recursion how to tell if list has even or odd length? Instead of checking the # after finding length, do both at the same time! ----------- MUTUAL RECURSION even-length: (-> ((list T)) boolean) (even-length? '(7)) ==> #f (even-length? '(1 2 3 4)) ==> #t ----------- these examples don't tell how to break up into car and cdr What examples would? ---------- (even-length? '()) ==> #t (even-length? '(4)) ==> #f (even-length? '(3 4)) ==> #t ---------- can tell if lst is even-length if lst is '() or (cdr lst) is odd length Use same reasoning to do odd-length ----------- (define even-length? (lambda (lst) (or (null? lst) (odd-length? (cdr lst))))) (define odd-length? (lambda (lst) (and (not (null? lst)) (even-length? (cdr lst))))) ----------- Won't do to test if (cons 1 lst) is even length! although that is logically true, makes infinite loop...