CS 227 Lecture -*-Outline -*- focus: recursion over different kinds of types, and their relation to recursion the recursive definition of those types. * List representation of Trees (4.4) The book calls this "Tree rep of lists" but that is backwards This is a summary of the previous section (tree or deep recursion) First will explain why we call lists with sublists trees ** how lists lists represent trees in computer science we draw trees upside down (root system) *** examples these are trees of symbols ------------------------- TREES REPESENTED BY LISTS (a b c) a b c ------------------------- ------------------------- ((a b) (() d)) (a b) (() d) a b () d ------------------------ ------------------------- (a (b c d) ((e ()) g)) a (b c d) ((e ()) g) b c d (e ()) g e () ------------------------ ------------------------ () ------------------------- *** terminology point out on pictures too def: the *nodes* of a tree are the whole list and each item, top-level and nested, in the list def: the *root* of a tree is the item at nesting level 0 def: a *branch* connects a list's node to the node for each of its top-level items def: a (proper) *subtree* is a tree nested inside another tree def: a *leaf* in a (tree T) is a node that is a T e.g., in a tree of atomic-items, () is not a leaf but in a (tree atom), () is a leaf def: the *depth* of a tree is the maximum nesting level of its leaves *** abstract view of trees and implementation ------------- TREE VALUES Def: let T be a type of values. A (tree T) is either () or (x t1 t2 ...) or (t1 t2 ...) where x has type T and t1, t2 ... have type (tree T) ------------- this is the external representation too ------------- TREE OBJECTS REPRESENTATION VIEW: Def: let T be a type of object. A (tree T) object is either '() or (cons x t) or (cons t1 t2) where x has type T and t1, t2 have type (tree T) AS AN ABSTRACT DATA TYPE: Def: let T be a type of object. A (tree T) object is either the-empty-tree or (tree-cons x t) or (tree-cons t1 t2) where x has type T and t1, t2 have type (tree T) ------------- the book (and us in 4.3) took the representation view we will consider the abstract view as a way of explaining trees better *** basic procedures for trees ------------ BASIC PROCEDURES for TREES the-empty-tree: (tree T) tree-cons: (-> ((or T (tree T)) (tree T)) (tree T)) tree-empty?: (-> ((tree T)) boolean) tree-first: (-> ((tree T)) (or T (tree T))) ; REQUIRES: the tree is not empty tree-rest: (-> ((tree T)) (tree T)) ; REQUIRES: the tree is not empty (define the-empty-tree '()) (define tree-cons cons) (define tree-empty? null?) (define tree-first car) (define tree-rest cdr) ------------ *** use in programming **** sum-all expressed with tree operations -------------- (define sum-all ; TYPE: (-> ((tree number)) number) (lambda (ton) ; ENSURES: result is the sum of ; all the numbers in ton (cond ((tree-empty? ton) 0) ((number? (tree-first ton)) (+ (tree-first ton) (sum-all (tree-rest ton)))) (else (+ (sum-all (tree-first ton)) (sum-all (tree-rest ton)))) ))) -------------- **** tdepth -------------- (tdepth '()) ==> 0 (tdepth '(a)) ==> 1 (tdepth '(a (b ((c))))) ==> 4 (tdepth '((e f) g)) ==> 2 (tdepth '((e () (f)))) ==> 3 (tdepth '(() (e () (f)))) ==> 3 tdepth: (-> ((tree atomic-item)) number) -------------- base case, return 0 flat case: tree-first of tree is an atomic-item have to get (tdepth tree) from (tdepth (tree-first tree)) = 1 (tdepth (tree-rest tree)) e.g. if tree is (a (b ((c)))) have to get 4 from (tdepth (tree-first tree)) = 1 and (tdepth (tree-rest tree)) = (tdepth '((b ((c))))) = 4 so looks like could take (tdepth (tree-rest tree)) but if tree is (a) thn have to get 1 from (tdepth (tree-first tree)) = 1 and (tdepth (tree-rest tree)) = (tdepth '()) = 0 so (tdepth (tree-rest tree)) doesn't work. By more examples or definition (involves maximum) can see that the code is: (max 1 (tdepth (tree-rest tree))) tree case: tree-first of tree is a subtree have to get (tdepth tree) from (tdepth (tree-first tree)) (tdepth (tree-rest tree)) e.g. if tree is ((e () (f))) have to get 2 from (tdepth (tree-first tree)) = (tdepth '(e () (f))) = 2 (tdepth (tree-rest tree)) = (tdepth '()) = 0 so looks like can use (add1 (tdepth (tree-first tree))) but consideration of examples like (tdepth '(() (e () (f)))) ==> 3 in which (add1 (tdepth (tree-first tree))) = 1 but (tdepth (tree-rest tree)) (tdepth '((e () (f)))) = 3 show that have to take the max of these Another way to see this is by the definition and the fact that the tree-first is nested nested but the tree-rest is not, so code is: (max (add1 (tdepth (tree-first tree))) (tdepth (tree-rest tree))) ------------- (DEFINE tdepth ; TYPE: (-> ((tree atomic-item)) number) (LAMBDA (tr) (COND [(tree-empty? tr) 0] [(atomic-item? (tree-first tr)) (max 1 (tdepth (tree-rest tr)))] [ELSE (max (add1 (tdepth (tree-first tr))) (tdepth (tree-rest tr)))]))) ------------- show trace of this (load "tdepth-trace.ss") show what happens if call it on a symbol (error) **** remove-left-most --------------- (remove-left-most 'b '(a (b c) () (c (b a)))) ==> (a (c) () (c (b a))) (remove-left-most 'c '(a (b c) () (c (b a)))) ==> (a (b) () (c (b a))) (remove-left-most 'a '()) ==> () remove-left-most: (-> (atomic-item (tree atomic-item)) (tree atomic-item)) What is the base case? the flat case? the tree case? Write it using basic procedures for trees --------------- Have them do this in groups base case: return '() flat case: if tree-first is an atomic-item if equal to item, return the rest of tree otherwise, recurse on rest of tree tree case: if first of tree is a pair or null? have to recurse in first and rest, but only want to remove item once so depends on where it is: first or rest use member-all? to test (DEFINE remove-left-most ; TYPE: (-> ((atomic-item ; (tree atomic-item))) ; (tree atomic-item)) (LAMBDA (item tr) (COND [(tree-empty? tr) '()] [(atomic-item? (tree-first tr)) (COND [(equal? item (tree-first tr)) (tree-rest tr)] [ELSE (tree-cons (tree-first tr) (remove-left-most item (tree-rest tr)))])] [ELSE (COND [(member-all? item (tree-first tr)) (tree-cons (remove-left-most item (tree-first tr)) (tree-rest tr))] [ELSE (tree-cons (tree-first tr) (remove-left-most item (tree-rest tr)))])]))) improve this on-line (simplify) the one in the book works for all datums so in tree case consider if equal to item, return the rest of tr *** summary: relation of definiton of tree to standard form --------------- SUMMARY OF TREE RECURSION for (tree atomic-item) DEFINITION STANDARD FORM of proc (tree (LAMBDA (tr) atomic-item) (COND either () [(tree-empty? tr) ...] or (x t1) [(atomic-item? (tree-first tr)) ...] or (t1 t2) [ELSE ...])) where x has type atomic-item and t1, t2 have type (tree atomic-item) --------------- now compare and generalize, taking away the tree abstraction to get back to the form of last time and the book --------------------- SUMMARY OF TREE RECURSION for (tree T) (representation view) DEFINITION STANDARD FORM of proc (tree T) (LAMBDA (tr) is (COND either () [(null? tr) ...] or (x t1) [(T? (car tr)) ...] or (t1 t2) [ELSE ...])) where x has type T and t1, t2 have type (tree T) and T?: (-> (datum) boolean) is such that (T? x) = #t ---------------- ** recursion over the type s-expression ---------------- S-EXPRESSIONS examples: () a 1 (a b 1) (a . 3) (() (c) (e f () h)) Def: an s-expression is either a or (cons s1 s2) where a has type atom, and s1, s2 have type s-expression. Theorem: x is an s-expression if and only if x has type (or atom (pair s-expression s-expression)). ---------------- all scheme expressions have this structure: (+ 3 (* 5 7)) but this includes much more, such as doted pairs. *** depth the depth program in the book is more general than tdepth it works on atoms as arguments ------------ depth: (-> (s-expression) number) (depth 'a) ==> 0 ------------ base case is whether the item is an atom (recall this includes '()) return 0 the recursion in the recursive case goes down both car and cdr since will take car of the case when the car is an atom in the base case ----------- (DEFINE depth ; TYPE: (-> (s-expression) number) (LAMBDA (item) ; ENSURES: result is the maximum depth ; of item (COND [(atom? item) 0] [(pair? item) (max (add1 (depth (car item))) (depth (cdr item)))]))) ----------- *** summary for atom or tree recursion --------------- SUMMARY OF S-EXPRESSION RECURISON DEFINITION STANDARD FORM of proc a s-expression (LAMBDA (item) is either (COND a [(atom? item) ...] or (cons s1 s2) [(pair? item)...])) where a has type atom and s1, s2 have type s-expression ---------------- ** comparison to other type (data-driven recursion) ---------------- SUMMARY OF RECURISON for the type (flat) (list T) DEFINITION STANDARD FORM of proc a (list T) (LAMBDA (ls) is either: (COND () [(null? ls) ...] or (cons x l) [ELSE ...])) where x has type T and l has type (list T) a (non-empty-list (LAMBDA (ls) T) is either: (COND (cons x '()) [(null? (cdr ls)) ...] or (cons x l) [ELSE ...])) where x has type T and l has type a (non-empty-list T) ---------------- from this we can generalize to other recursively defined types -------------- GENERALIZING TO ANOTHER TYPE BASIC PROCEDURES FOR type N z: N s: (-> (N) N) p: (-> (N) N) z?: (-> (N) boolean) (z? z) = #t (z? (s n)) = #f (p (s n)) = n DEFINITION STANDARD FORM of proc An N is either z or (s n) where n is an N ------------ fill in the standard form! we can program add to like append ----------- add: (-> (N) N) (add z n2) = n2 (add (s n) n2) = (s (add n n2)) -------------- (DEFINE add (LAMBDA (n1 n2) (COND [(z? n1) n2] [ELSE (s (add (p n1) n2))] ))) this is flat recursion over N Can think of N as the natural numbers! p = sub1 s = add1 z = 0 z? = zero?