Let's continue with the same data definitions from Wednesday.

A parent is a structure
  (make-parent n y e loc)
where n and e are symbols, y is a num, and loc is a list-of-children. 

(define-struct parent (name year eye-color children))

Notice however, that we don't have a data definition for
list-of-children.  What should we use?

A list-of-children is either
  - empty, or
  - (cons p loc)
    where p is a parent and loc is a list-of-children

Write a program at-least-two-children which consumes a parent and
returns a list of names of all parents with at least two children.

;; at-least-two-children : parent -> list-of-symbol
;; returns a list of all people in tree with at least two children
(define (at-least-two-children a-parent)
  (cond [(> (num-children (parent-children a-parent)) 2)
	 (cons (parent-name a-parent) 
	       (children-with-two-children (parent-children a-parent)))]
	[else (children-with-two-children (parent-children a-parent))]))

;; children-with-two-children : list-of-children -> list-of-symbol
;; returns a list of all children with at least two children
(define (children-with-two-children a-loc)
  (cond [(empty? a-loc) empty]
	[(cons? a-loc)
	 (append (at-least-two-children (first a-loc))
		 (children-with-two-children (rest a-loc)))]))

;; num-children : list-of-children -> num
;; counts how many children are in the list
(define (num-children a-loc)
  (cond [(empty? a-loc) 0]
	[else (+ 1 (num-children (rest a-loc)))]))

Write the program find-birth-year, which consumes a parent and a
symbol and returns a number.  The returned number is the year in which
the named person was born.  Assume that the named person does appear
in the family tree.

;; find-birth-year : parent sym -> num
;; determine the birth year of the named person
(define (find-birth-year a-parent name)
  (cond [(symbol=? (parent-name a-parent) name)
	 (parent-year a-parent)]
	[else (find-year-in-children (parent-children a-parent) name)]))

;; find-year-in-children : list-of-children sym -> num
;; finds the birth year of the named person in a list of children
(define (find-year-in-children a-loc name)
  (cond [(empty? a-loc) ...]
	[(cons? a-loc)
	 ... (find-birth-year (first a-loc) name) ...
	 (find-year-in-children (rest a-loc) name) ... ]))

Notice that it's not clear how to fill in the template for
find-year-in-children.  What do we put into the base case?  There is
no meaningful number to return if the child list becomes empty.  Note
also that, even though we've assumed that the named person does appear
in the tree, we don't know which list-of-children that person is in.
So DrScheme may reach this case while running find-birth-year.

We need some way to indicate that we did not find the person we're
looking for in a given list of children.  False is a good choice, but
returning false would violate our contract.  However, in this case we
can convince ourselves that our original contract was indeed wrong.
Both functions must be able to return either a num or false.

With this in mind, here are the corrected versions.  Note: in this
version, find-birth-year uses a helper function called
find-year-in-children instead of find-parent (as we started in class).
The find-parent version appears at the end of the lecture notes.  This
version is cleaner and clearer, as you should be able to see from the
code.

;; A num-or-false is either
;;   - a number, or
;;   - false

;; find-birth-year : parent sym -> num-or-false
;; determine the birth year of the named person
(define (find-birth-year a-parent name)
  (cond [(symbol=? (parent-name a-parent) name)
	 (parent-year a-parent)]
	[else (find-year-in-children (parent-children a-parent) name)]))

;; find-year-in-children : list-of-children sym -> num-or-false
;; finds the birth year of the named person in a list of children
(define (find-year-in-children a-loc name)
  (cond [(empty? a-loc) false]
	[(cons? a-loc)
	 (select-num-input 
	  (find-birth-year (first a-loc) name) 
	  (find-year-in-children (rest a-loc) name))]))

;; select-num-input : num-or-false num-or-false -> num-or-false
;; returns whichever input is a num, or false if neither one is
(define (select-num-input n-or-f1 n-or-f2)
  (cond [(number? n-or-f1) n-or-f1]
	[else n-or-f2]))

Notice what happened here.  Despite our assumption that the person
named is in the family tree, we still ended up writing a program that
works if the person is not in the family tree.  There are two morals
to this story: 

  - simplifying assumptions aren't always simplifying!
  - sometimes you find that your contracts are insufficient.  It is
    okay to change a contract while writing a program if you have
    convinced yourself that the original contract truly is wrong.

--------------------------------------------------------

As a side note, here is the version using find-parent.  Notice that
with this version, find-birth-year is never called recursively.  All
of the recursive calls are to find-parent instead.  

You should try at least one of these two versions of this program with 
the stepper.  All of the needed code is in this set of notes.

;; find-birth-year : parent sym -> num-or-false
;; determine the birth year of the named person
(define (find-birth-year a-parent name)
  (cond [(symbol=? (parent-name a-parent) name)
	 (parent-year a-parent)]
	[else (get-year-if-parent
	       (find-parent (parent-children a-parent) name))]))

;; get-year-if-parent : a-parent-or-false -> num-or-false
;; return birth year if input is a parent, false otherwise
(define (get-year-if-parent a-parent-or-false)
  (cond [(parent? a-parent-or-false) (parent-year a-parent-or-false)]
	[else false]))

;; find-parent : list-of-children sym -> parent-or-false
;; finds the named person in a list of children, or false if person not in list
(define (find-parent a-loc name)
  (cond [(empty? a-loc) false]
	[(cons? a-loc)
	 (cond [(parent? (find-parent-in-parent (first a-loc) name))
		(find-parent-in-parent (first a-loc) name)]
	       [else (find-parent (rest a-loc) name)])]))

;; find-parent-in-parent : parent -> parent or false
;; looks for a person in a tree; returns false if person not there
(define (find-parent-in-parent a-parent name)
  (cond [(symbol=? (parent-name a-parent) name) a-parent]
	[else (find-parent (parent-children a-parent) name)]))