Practice tree-shaped data using a simple Tree ADT. You will (1) predict results of tree recursion, (2) trace a recursive computation that builds a tree of calls, and (3) implement + extend classic tree-recursive functions (fringe, then a pipeline computation).
**Predict the output (4 lines)** before running. Write your prediction as comments first.
```python
# Tree ADT + example tree
def tree(lbl, branches=None):
if branches is None:
branches = []
return [lbl] + list(branches)
def label(t):
return t[0]
def branches(t):
return t[1:]
def is_leaf(t):
return len(branches(t)) == 0
t = tree(3, [
tree(1),
tree(2, [tree(1), tree(1)])
])
# Two classic tree recursions
def count_leaves(t):
if is_leaf(t):
return 1
return sum(count_leaves(b) for b in branches(t))
def find_path(t, target):
if label(t) == target:
return [label(t)]
for b in branches(t):
subpath = find_path(b, target)
if subpath is not None:
return [label(t)] + subpath
return None
print(find_path(t, 2))
print(find_path(t, 1))
print(find_path(t, 99))
print(count_leaves(t))
```
Questions:
1) What does each `print` line output?
2) Why does `find_path(t, 1)` return that particular path (hint: branch order)?In this exercise, the **shape of the returned tree mirrors the recursion**.
1) Trace the recursive calls for `fib_tree(4)`.
- List the calls in the order they are *initiated*.
- Then show the return value (the node label) for each call as it unwinds.
2) Predict (then verify):
- `label(fib_tree(4))`
- `count_leaves(fib_tree(4))`
Code:
```python
def tree(lbl, branches=None):
if branches is None:
branches = []
return [lbl] + list(branches)
def label(t):
return t[0]
def branches(t):
return t[1:]
def is_leaf(t):
return len(branches(t)) == 0
def count_leaves(t):
if is_leaf(t):
return 1
return sum(count_leaves(b) for b in branches(t))
def fib_tree(n):
if n <= 1:
return tree(n)
left = fib_tree(n - 1)
right = fib_tree(n - 2)
return tree(label(left) + label(right), [left, right])
ft4 = fib_tree(4)
print(label(ft4))
print(count_leaves(ft4))
```
Your trace should explain where the **base case** happens and how results are **combined**.This exercise has two parts.
**Part A (Implement)**: Implement `fringe(t)`, which returns a Python list of the **leaf labels** of a tree from left to right.
**Part B (Extend)**: Using `fringe(t)` as a *conventional interface* (a flat list of leaves), implement `sum_odd_squares(t)`:
- take the leaf labels
- keep only the odd ones
- square them
- sum the squares
Example goal:
- If the leaf labels are `[1, 2, 3, 4]`, then `sum_odd_squares` should return `1^2 + 3^2 = 10`.
Starter setup (Tree ADT + example trees):
```python
def tree(lbl, branches=None):
if branches is None:
branches = []
return [lbl] + list(branches)
def label(t):
return t[0]
def branches(t):
return t[1:]
def is_leaf(t):
return len(branches(t)) == 0
x = tree(0, [
tree(1),
tree(2, [tree(3), tree(4)]),
tree(5)
])
xx = tree(99, [x, x])
```
Keep your solutions short and respect the abstraction barrier (use `label`, `branches`, `is_leaf`).