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...