# Recursive Objects

Tips for navigating the slides:
• Press O or Escape for overview mode.
• Visit this link for a nice printable version
• Press the copy icon on the upper right of code blocks to copy the code

• Tree class

### Why do we need a new list?

Python lists are implemented as a "dynamic array", which isn't optimal for all use cases.

😭 Inserting an element is slow, especially near front of list:

What should we insert?

😭 Plus inserting too many elements can require re-creating the entire list in memory, if it exceeds the pre-allocated memory.

A linked list is a chain of objects where each object holds a value and a reference to the next link. The list ends when the final reference is empty.

What should we insert?

Linked lists require more space but provide faster insertion.

``````
empty = ()

def __init__(self, first, rest=empty):
self.first = first
self.rest = rest
``````

How would we use that?

``````
``````

``````
empty = ()

def __init__(self, first, rest=empty):
self.first = first
self.rest = rest

def __repr__(self):
if self.rest:
rest_repr = ', ' + repr(self.rest)
else:
rest_repr = ''
return 'Link(' + repr(self.first) + rest_repr + ')'

def __str__(self):
string = '<'
string += str(self.first) + ' '
self = self.rest
return string + str(self.first) + '>'
``````

It's built-in to code.cs61a.org and you can `draw()` any Link.

### Creating a range

Similar to `[x for x in range(3, 6)]`

``````
"""Return a Link containing consecutive integers
from START to END, not including END.
"""
if start >= end:
``````

### Exercise: Mapping a linked list

Similar to `[f(x) for x in lst]`

``````
"""Return a Link that contains f(x) for each x in Link LL.
>>> square = lambda x: x * x
"""
``````

### Exercise: Mapping a linked list (Solution)

Similar to `[f(x) for x in lst]`

``````
"""Return a Link that contains f(x) for each x in Link LL.
>>> square = lambda x: x * x
"""
``````

### Exercise: Filtering a linked list

Similar to `[x for x in lst if f(x)]`

``````
"""Return a Link that contains only the elements x of Link LL
for which f(x) is a true value.
>>> is_odd = lambda x: x % 2 == 1
"""
``````

### Exercise: Filtering a linked list (Solution)

Similar to `[x for x in lst if f(x)]`

``````
"""Return a Link that contains only the elements x of Link LL
for which f(x) is a true value.
>>> is_odd = lambda x: x % 2 == 1
"""
elif f(ll.first):
``````

Attribute assignments can change `first` and `rest` attributes of a `Link`.

``````
s.first = "Hi"
s.rest.first = "Hola"
s.rest.rest.first = "Oi"
``````

### Beware infinite lists

The rest of a linked list can contain the linked list as a sub-list.

``````
t = s.rest
t.rest = s
``````
``````
s.first
``````
``````
s.rest.rest.rest.rest.rest.first
``````

``````
"""Inserts NEW_VAL in front of LINKED_LIST,

>>> insert_front(ll, 0)
"""
``````

``````
"""Inserts NEW_VAL in front of LINKED_LIST,

>>> insert_front(ll, 0)
"""
``````

Insert
``````
"""Add NEW_VAL to ORDERED_LIST, returning modified ORDERED_LIST.
"""
if new_val < ordered_list.first:

elif new_val > ordered_list.first and ordered_list.rest is Link.empty:

elif new_val > ordered_list.first:

return ordered_list
``````

Insert
``````
"""Add NEW_VAL to ORDERED_LIST, returning modified ORDERED_LIST.
"""
if new_val < ordered_list.first:
original_first = ordered_list.first
ordered_list.first = new_val
elif new_val > ordered_list.first and ordered_list.rest is Link.empty:
elif new_val > ordered_list.first:
return ordered_list
``````

### Showdown: Python list vs. Link

The challenge:

• Store all the half-a-million words in "War and Peace"
• Insert a word at the beginning.

Version 10,000 runs 100,000 runs
Python list 2.6 seconds 37 seconds

Try it yourself on your local machine (Legit Python!): warandpeace.py

## Trees

### Tree concepts • A tree has a root label and a list of branches
• Each branch is itself a tree
• A tree with zero branches is called a leaf

### Trees: Data abstraction

This is what we've been using:

 `tree(label, branches)` Returns a tree with given LABEL at its root, whose branches are BRANCHES `label(tree)` Returns the label of root node of TREE `branches(tree)` Returns the branches of TREE (each a tree). `is_leaf(tree)` Returns true if TREE is a leaf node.

### Trees: Data abstraction

Using an implementation like this:

``````
def tree(label, branches=[]):
return [label] + list(branches)

def label(tree):
return tree

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

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

🤔 How could we represent trees as a Python class?

### A Tree class

``````
class Tree:
def __init__(self, label, branches=[]):
self.label = label
self.branches = list(branches)

def is_leaf(self):
return not self.branches
``````

🤔 What's different? What's the same?

### tree versus Tree

tree Tree
`t = tree(label, branches=[])` `t = Tree(label, branches=[])`
`branches(t)` `t.branches`
`label(t)` `t.label`
`is_leaf(t)` `t.is_leaf()`
``````
def fib_tree(n):
if n == 0 or n == 1:
return tree(n)
else:
left = fib_tree(n - 2)
right = fib_tree(n - 1)
fib_n = label(left) + label(right)
return tree(fib_n, [left, right])
``````
``````
def fib_tree(n):
if n == 0 or n == 1:
return Tree(n)
else:
left = fib_tree(n - 2)
right = fib_tree(n - 1)
fib_n = left.label + right.label
return Tree(fib_n, [left, right])
``````

### A fancier Tree

This is what assignments actually use:

``````
class Tree:

def __init__(self, label, branches=[]):
self.label = label
for branch in branches:
assert isinstance(branch, Tree)
self.branches = list(branches)

def is_leaf(self):
return not self.branches

def __repr__(self):
if self.branches:
branch_str = ', ' + repr(self.branches)
else:
branch_str = ''
return 'Tree({0}{1})'.format(self.label, branch_str)

def __str__(self):
return '\n'.join(self.indented())

def indented(self):
lines = []
for b in self.branches:
for line in b.indented():
lines.append('  ' + line)
return [str(self.label)] + lines
``````

It's built in to code.cs61a.org, and remember, you can `draw()` any tree/Tree.

## Tree mutation

### Doubling a Tree ``````
def double(t):
"""Doubles every label in T, mutating T.
>>> t = Tree(1, [Tree(3, [Tree(5)]), Tree(7)])
>>> double(t)
>>> t
Tree(2, [Tree(6, [Tree(10)]), Tree(14)])
"""
t.label = t.label * 2
for b in t.branches:
double(b)
``````

### Exercise: Pruning trees Removing subtrees from a tree is called pruning.

Always prune branches before recursive processing.

``````
def prune(t, n):
"""Prune all sub-trees whose label is n.
>>> t = Tree(3, [Tree(1, [Tree(0), Tree(1)]), Tree(2, [Tree(1), Tree(1, [Tree(0), Tree(1)])])])
>>> prune(t, 1)
>>> t
Tree(3, [Tree(2)])
"""
t.branches = [___ for b in t.branches if ___]
for b in t.branches:
prune(___, ___)
``````

### Exercise: Pruning trees (Solution) Removing subtrees from a tree is called pruning.

Always prune branches before recursive processing.

``````
def prune(t, n):
"""Prune all sub-trees whose label is n.
>>> t = Tree(3, [Tree(1, [Tree(0), Tree(1)]), Tree(2, [Tree(1), Tree(1, [Tree(0), Tree(1)])])])
>>> prune(t, 1)
>>> t
Tree(3, [Tree(2)])
"""
t.branches = [b for b in t.branches if b.label !=n]
for b in t.branches:
prune(b, n)
``````

### Recursive objects

Why are `Tree` and `Link` considered recursive objects?

Each type of object contains references to the same type of object.

• An instance of `Tree` can contain additional instances of `Tree`, in the `branches` variable.
• An instance of `Link` can contain an additional instance of `Link`, in the `rest` variable.

Both classes lend themselves to recursive algorithms. Generally:

• For `Tree`: The base case is when `is_leaf()` is true;
the recursive call is on the `branches`.
• For `Link`: The base case is when the rest is `empty`;
the recursive call is on the `rest`.

## Python Project of The Day!

### Exotic Trees

A Field Guide to Exotic Trees: A gallery of trees programmatically generated from a Python script. 