Trees - Fri, Sep 27

Remember to review the last two lectures that you skipped!

Box-and-Pointer Notation

The Closure Property of Data Types

A method for combining data values satisfies the closure property if the result of the combination can itself be combined using the same method.

Closure is powerful because it permits us to create hierarchical structures. Hierarchical structures are made up of parts, which themselves are made up of parts, and so on.

For example, lists can contain lists, which can contain lists, and so on.

Processing Container Values

Sequence Aggregation

Several built-in functions take iterable arguments and aggregate them into a value.

# Return sum of an iterable of numbers, plus value of start (zero by default). If iterable is empty, return start.
sum(iterable[, start])

# Return the largest item, after applying func (identity by default).
max(iterable[, key=func])

# Return the largest iterable, after applying func (identity by default).
max(i1, i2, i3, ...[, key=func])

# Return True if bool(x) is True for all values x in the iterable. If iterable is empty, return True.
all(iterable)

# Return True if bool(x) is True for any values x in the iterable. If iterable is empty, return True.
any(iterable)

Trees

People often refer to labels by their locations: "each parent is the sum of its children."

Recursive Description (Wooden Trees)

A tree has a root label and a list of branches, where each branch is a tree. A tree starts at the root, and a tree with zero branches is called a leaf.

Relative Description (Family Trees)

Each location in a tree is called a node, and each node has a label that can be any value. One node can be the parent/child of another, and the top node is the root node.

A path is a sequence of nodes, typically from a root to a leaf. Each such path is unique.

Implementing the Tree Abstraction

>>> tree(3, [tree(1),
...          tree(2, [tree(1),
...                   tree(1)])])
[3, [1], [2, [1], [1]]]
def tree(label, branches=[]):
    for branch in branches:
        assert is_tree(branch)
    return [label] + list(branches)

def label(tree):
    return tree[0]

def branches(tree):
    return tree[1:]

def is_tree(tree):
    if type(tree) != list or len(tree) < 1:
        return False
    for branch in branches(tree):
        if not is_tree(branch):
            return False
    return True

def is_leaf(tree):
    return not branches(tree)

The draw(list) function on code.cs61a.org allows us to visualize any list, no matter how deep. Using the code written above, draw(list) will display a tree -- rather than boxed lists, it will display tree "leaves." The autodraw() function will draw any list or tree created.

Tree Processing

Recursion!

def count_leaves(t):
    if is_leaf(t):
        return 1
    else:
        return sum([count_leaves(b) for b in branches(t)])