Trees
Trees
Last time, we closed by talking about defining a datatype for tree data. Our example was the family tree of George III of the United Kingdom:

We defined a datatype for trees:
data AncTree: | person(name :: String, eye-color :: String, mother :: AncTree, father :: AncTree) end
Let’s write George’s family tree:
george-tree = person("George III", "green", person("Auguste", "green", person("Magdalena", "blue", person("Sophia", "green", ...
What should we put for Sophia’s parents? Does ""
work?
We’re going to need to define a new constructor:
data AncTree: | person(name :: String, eye-color :: String, mother :: AncTree, father :: AncTree) | unknown end
So now we can say
george-tree = person("George III", "green", person("Auguste", "green", person("Magdalena", "blue", person("Sophia", "green", unknown, unknown), unknown), person("Friedrich II", "brown", unknown, unknown)), person("Frederick", "green", unknown, unknown))
Now that we’ve defined this data type, how can we use it? Let’s write a function to find a specific name in the tree:
fun in-tree(name :: String, tree :: AncTree) -> Boolean: ... where: in-tree("George III", george-tree) is true in-tree("Sophia", george-tree) is true in-tree("Doug", george-tree) is false end
How are we going to complete this function? We’re dealing with a datatype, so we
know we’re going to need cases
:
fun in-tree(name :: String, tree :: AncTree) -> Boolean: cases (AncTree) tree: | unknown => ... | person(nm, ec, mo, fa) => ... end where: in-tree("George III", george-tree) is true in-tree("Sophia", george-tree) is true in-tree("Doug", george-tree) is false end
What should we put in each case?
fun in-tree(name :: String, tree :: AncTree) -> Boolean: cases (AncTree) tree: | unknown => false | person(nm, ec, mo, fa) => (nm == name) or in-tree(name, mo) or in-tree(name, fa) end where: in-tree("George III", george-tree) is true in-tree("Sophia", george-tree) is true in-tree("Doug", george-tree) is false end
This suggests a new template for ancestor-tree processing functions (like our template for list processing functions, but for trees):
fun fun-name(..., tree :: AncTree) -> ...: cases (AncTree) tree: | unknown => ... | person(nm, ec, mo, fa) => ... fun-name(..., mo) ... fun-name(..., fa) end end
Let’s take a careful look at our function. This function is recursive–it’s calling itself on the “mother” and “father” subtrees. What happens when we call:
in-tree("Friedrich II", george-tree)
(See lecture capture for details)
More tree functions
What if we wanted to find the total number of people in the tree with a particular eye color?
fun count-eye-color(color :: String, tree :: AncTree) -> Number: cases (AncTree) tree: | unknown => 0 | person(nm, ec, mo, fa) => if ec == color: 1 + count-eye-color(color, mo) + count-eye-color(color, fa) else: count-eye-color(color, mo) + count-eye-color(color, fa) end end end
How about getting a list of all of the names in the tree?
fun list-names(tree :: AncTree) -> List<String>: cases (AncTree) tree: | unknown => empty | person(nm, ec, mo, fa) => link(nm, L.append(list-names(mo), list-names(fa)) end end
In what order will this produce the names in the list? What if we wanted a different order?
Can we count the generations in the tree?
fun generations(tree :: AncTree) -> Number: cases (AncTree) tree: | unknown => 0 | person(nm, ec, mo, fa) => 1 + num-max(generations(mo), generations(fa)) end end