? ? ? ? ? ? \ / \ / \ / 'jackie 'mona 'abe (1926) ? (1929) (1920) \ / \ / 'marge 'homer (1956) (1955) \ / 'bart (1979)(Note: We don't know the name of Grampa Bouvier, marge's father, but he died in a roller-coaster accident.) (Birthdates fabricated, but names courtesy of springfield nuclear power plant's web site.) Miscellaneous vocabulary word: While Abe was an enlisted man in WWII, his squad, the flying hellfish, found a a cache of stolen paintings, and they formed a tontine ("an investment plan in which participants buy shares in a common fund and recevie an annuity, with the entire fund going to the final survivor or to those who survive after a specified time. [Fr., after Lorenzo Tonti (1635-90?), Naples-born French banker]." Source: American Heritage College Dictionary)
? ? \ / 'jackie (1926)and
? ? \ / 'jackie (1926) ? \ / 'marge (1958)What might be considered the simplest possible family tree?
Definition: a FamTree is
'unknown
, or
(make-child symbol
FamTree FamTree number)
,
where the symbol
represents a name, the first FamTree is the mother's family tree,
the second FamTree is the father's side,
and the number is the birthyear.
'unknown
.
to do:: write the define-struct for child.
Examples:
(define stone-age -2000000)
(make-child 'barney 'unknown 'unknown (+ stone-age 120))
(make-child 'bam-bam (make-child 'barney 'unknown 'unknown (+ stone-age 120)) (make-child 'betty 'unknown 'unknown (+ stone-age 125)) (+ stone-age 150))
'unknown
? ? ? ? ? ? \ / \ / \ / 'jackie 'mona 'abe (1926) ? (1929) (1920) \ / \ / 'marge 'homer (1956) (1955) \ / 'bart (1979)
(make-child 'bart (make-child 'marge (make-child 'jackie 'unknown 'unknown 1926) 'unknown 1956) (make-child 'homer (make-child 'mona 'unknown 'unknown 1929) (make-child 'abe 'unknown 'unknown 1920) 1955) 1979)Note how really, this is just writing the tree sideways! Of course, you can use placeholders to save typing:
(define abe-tree (make-child 'abe 'unknown 'unknown 1920)) (define mona-tree (make-child 'mona 'unknown 'unknown 1929)) (define homer-tree (make-child 'homer mona-tree abe-tree 1955)) (define jackie-tree (make-child 'jackie 'unknown 'unknown 1926)) (define marge-tree (make-child 'marge jackie-tree 'unknown 1956)) (define bart-tree (make-child 'bart marge-tree homer-tree 1979))
purpose | list functions | tree functions (provided to you) | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
pre-existing values | empty | 'unknown | |||||||||||||||||||||||||||||||
build one out of smaller parts | cons : any, list --> list |
make-child: symbol, FamTree, FamTree, number --> child |
|||||||||||||||||||||||||||||||
selectors |
|
|
|||||||||||||||||||||||||||||||
predicates |
|
|
;; famTree-func: FamTree --> ??? ;; (define (famTree-func a-ft) )
;; (size ft) : FamTree --> number ;; Return how many (non-unknown) children ft contains. ;; (define (size a-ft) (cond [(unknown? a-ft) ...] [(child? a-ft) ..(child-name a-ft)..(child-birthyear a-ft).. ..(size (child-ma a-ft))..(size (child-pa a-ft))..])) (= (size jackie-tree ..) (= (size bart-tree) ..) ; Other test cases? One important test is missing!What type of thing is
(child-ma a-ft)
(list, number, FamTree, symbol, ...?)
size
on them, as indicated
(size (child-ma a-ft))
(list, number, FamTree, symbol, ...?)
'unknown
s?)
;; (related-to-abe? ft): FamTree --> boolean ;; Is 'abe the name of anybody in the FamTree "ft"? ;; (Perhaps "descendent-of-abe?" would be a better name.) ;; (define (related-to-abe? a-ft) (cond [(unknown? a-ft) ...] [(child? a-ft) ..(child-name a-ft)..(child-birthyear a-ft).. ..(related-to-abe? (child-ma a-ft)).. ..(related-to-abe? (child-pa a-ft))..])) (boolean= (related-to-abe? jackie-tree) ...) (boolean= (related-to-abe? bart-tree) ...)Ask yourself the same questions as before, when filling in the parts.
;; cfa-version1 : FamTree --> num ;; Count how many female ancestors are in the tree a-ftn. ;; (define (cfa-version1 a-ftn) (cond [(unknown? 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.
;; cfa-version2 : FamTree --> num ;; Count how many female ancestors are in a-ftn. ;; (define (cfa-version2 a-ftn) (cond [(unknown? a-ftn) 0] [else (+ (count-mother (child-mother a-ftn)) (cfa (child-mother a-ftn)) (cfa (child-father a-ftn)))])) ;; count-mother : FamTree --> num ;; Determines how many ancestors to add based on the root of a-ftn ;; -- either 0 or 1. ;; (define (count-mother a-ftn) (cond [(unknown? a-ftn) 0] [else 1]))What if we wanted to count only female ancestors born after 1950? What in the above program changes? Only the helper function, which would change as follows:
;; count-if-young-enough: child -> num ;; Returns 1 if node has blue eyes, else 0. ;; (define (count-if-young-enough a-child) (cond [(< 1950 (child-birth-year a-child)) 1] [else 0])) ;; count-mother : FamTree -> num ;; Determines how many female ancestors to add based on a-ftn. ;; (define (count-mother a-ftn) (cond [(empty? a-ftn) 0] [else (count-if-young-enough a-ftn)]))Note that you're welcome to coalesce these two functions. If you want, you can even collapse the nested conds. But be careful -- if you start collapsing nested conds, it opens the door for a mistake (you collapse them incorrectly), and it becomes less clear to the reader that you are doing your case analysis correctly.
Finally, here is a version which doesn't require looking inside the mother's FamTree to see if it's unknown. Instead, it uses an extra argument.
;; cfa-version3 : FamTree --> num ;; Count how many female ancestors are in a-ftn. ;; ;; How it works [this is NOT part of the purpose]: ;; just call help-cfa, and indicate that the root of the tree ;; is not to be considered a female ancester. ;; (If you have extra knowledge that the person at the root ;; of a-ft is female, and you want to include her in the count, ;; use 1 instead of 0.) ;; (define (cfa-version3 a-ft) (help-cfa a-ft 0)) ;; help-cfa: FamTree, num --> num ;; Return how many female ancestors are in a tree, ;; where the root itself is either considered 0 or 1 ;; female ancestors; this 0 or 1 is provided ;; as the argument "mothers-at-root". ;; ;; How it works [this paragraph is NOT part of the purpose]: ;; This extra info is provided by the person who called us -- since they ;; might know whether our root is somebody else's mother.) ;; When we make a recursive call, we know whether each subtree ;; has our mother at the root, or our father, and we can ;; call with mothers-at-root set appropriately. ;; (define (help-cfa a-ft mothers-at-root) (cond [(unknown? a-ft) 0] [(child? a-ft) (+ mothers-at-root (help-cfa (child-pa a-ft) 0) (help-cfa (child-ma a-ft) 1))]))This use of an extra argument is actually a limited case of what we call an "accumulator"; we'll talk about the technique of using accumulators later, in lecture 15.
Note that it's fine to include comments how a function works. However, always distinguish "what a function computes" from "how it computes it". The first matters to others, the second doesn't.
Finally, a note on programming style: count-female-ancestors is a bit tricky to get right, the way we need to figure out whether we're approaching a sub-tree from the mother's side, or the father's side. Why is this? Well, sometimes problems inherently require some roundabout work. In this case, though, trying to count female ancestors would have been trivial, if we'd also known the gender of each child. There are two contradictory factors to be balanced:
;; path-to-mona: FamTree --> list-of-symbol, or false ;; Return a series of names leading from a-ft back ;; to an ancester named 'mona, or false if no such ancester. ;; (If multiple ancestors with that name, ;; a path to any is okay.) ;;
;; height: FamTree --> boolean ;; Return the maximum number of generations ;; which can be traced back along some path. ;; Somebody where we know the name, but no parents, counts as one generation. ;; (define (height a-ft) (cond [(unknown? a-ft) ...] [(child? a-ft) ... ])) (= (height 'unknown) 0) (= (height barney-tree) 1) (= (height bart-tree) 3)(solution)
;; (ivy ht): number --> famTree ;; Return a tree with height "ht", ;; but it's a stringy tree, with as few nodes as possible. ;;
;; (shrubbery ht): number --> famTree ;; Return a tree with height "ht", ;; but it's a bushy tree, with as many nodes as possible. ;;