Now that you know how to write recursive programs, you have the foundations needed to start writing interesting programs. Going to start today by studying family trees. -----------------| | V | --> Susan ->Tom | / ^ ^ | ^ | / / /----\- / Pat Mike Ann Joe ^ ^ \ / Mary This family tree starts at the child and indicates who a person's parents are. What would be a good data definition for such a family tree? Note that this is where CS starts to get creative: in how to design representations of information. A ft is either - a symbol, or - (make-node name father mother) where name is a symbol and father and mother are ft. (define-struct node (name father mother)) What's are examples of fts? 'Mary (make-node 'Ann 'Susan 'Tom) (make-node 'Mary (make-node 'Ann 'Susan 'Tom) 'Joe) What would a template look like here? (define (ft-func a-ft) (cond [(symbol? a-ft) ...] [(node? a-ft) ... (node-name a-ft) ... (ft-func (node-father a-ft)) ... (ft-func (node-mother a-ft)) ... ])) Write the program in-family?, which consumes an ft and a symbol and produces a boolean indicating whether a person with that name is in the tree. ;; compare-names : sym sym -> bool ;; determine whether two names are the same (define (compare-names name1 name2) (symbol=? name1 name2)) ;; in-family? : ft sym -> bool ;; determine whether a person appears in a family tree (define (in-family? a-ft name) (cond [(symbol? a-ft) (compare-names a-ft name)] [(node? a-ft) (or (compare-names (node-name a-ft) name) (in-family? (node-father a-ft) name) (in-family? (node-mother a-ft) name))])) This model of family trees is pretty simple because it only includes people's names. Let's make two changes to the model. First, we want to record additional information, such as each person's year of birth and eye-color. Second, we want to be able to leave either parent unspecified (since we don't always have information about both parents). How would we revise the data definition? The main difference between the previous definition and this one is that the nodes are now more complicated. A ftn (for family tree node) is either - empty, or - (make-child na f m yr ec) where na is a sym, f and m are ftn, yr is a num and ec is a sym (define-struct child (name father mother year eye-color)) What are some examples of this style of family tree? empty (make-child 'Mary (make-child 'Ann empty empty 1950 'blue) empty 1975 'green) What does the template look like? (define (ftn-func a-ftn) (cond [(empty? a-ftn) ...] [(child? a-ftn) ... (child-name a-ftn) ... (ftn-func (child-father a-ftn)) ... (ftn-func (child-mother a-ftn)) ... (child-year a-ftn) ... (child-eye-color a-ftn) ... ])) What does the in-family? program look like on this data definition? ;; in-family? : ft sym -> bool ;; determine whether a person appears in a family tree (define (in-family? a-ftn name) (cond [(empty? a-ftn) false] [(child? a-ftn) (or (compare-names (child-name a-ftn) name) (in-family? (child-father a-ftn) name) (in-family? (child-mother a-ftn) name))])) Write the program count-female-ancestors (a person does not count as his/her own ancestor). ;; cfa : ftn -> num ;; count how many female ancestors are in the tree (define (cfa a-ftn) (cond [(empty? a-ftn) 0] [else (cond [(empty? (child-mother a-ftn)) (cfa (child-father a-ftn))] [else (+ 1 (cfa (child-mother a-ftn)) (cfa (child-father a-ftn)))])])) Anything wrong with this program? Yes. It violates our rule of opening only one data definition per program. This one looks at the cases in the mother sub-tree. Therefore, we should use a helper function, as follows. ;; count-mother : ftn -> num ;; determines how many ancestors to add based on ftn (define (count-mother a-ftn) (cond [(empty? a-ftn) 0] [else 1])) ;; cfa : ftn -> num ;; count how many female ancestors are in the tree (define (cfa a-ftn) (cond [(empty? a0ftn) 0] [else (+ (count-mother (child-mother a-ftn)) (cfa (child-mother a-ftn)) (cfa (child-father a-ftn)))])) What if we wanted to count only blue-eyed female ancestors? What in the above program changes? Only the helper function, which would change as follows: ;; count-if-blue-eyes : child -> num ;; returns 1 if node has blue eyes, else 0 (define (count-if-blue-eyes a-child) (cond [(symbol=? 'blue (child-eye-color a-child)) 1] [else 0])) ;; count-mother : ftn -> num ;; determines how many blue-eyed ancestors to add based on ftn (define (count-mother a-ftn) (cond [(empty? a-ftn) 0] [else (count-if-blue-eyes a-ftn)]))