CS61A(19): Composition

This is the lecture note of CS61A - Lecture 19.

Linked Lists

A linked list is either empty or a first value and the rest of the linked list.

Let's implement this data structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Link:
"""A linked list.

>>> s = Link(3, Link(4, Link(5)))
>>> s
Link(3, Link(4, Link(5)))
>>> s.first
3
>>> s.rest
Link(4, Link(5))
>>> s.rest.first
4
>>> s.rest.first = 7
>>> s
Link(3, Link(7, Link(5)))
>>> s.first = 6
>>> s.rest.rest = Link.empty
>>> s
Link(6, Link(7))
"""

empty = ()

def __init__(self, first, rest=empty):
assert (rest is Link.empty) or isstance(rest, Link)
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 = '<'
while self.rest is not Link.empty:
string += str(self.first) + ' '
self = self.rest
return string + str(self.first) + '>'

Linked List Processing

Let's see an example.

1
2
3
4
5
6
7
# Example: Range, Map, and Filter for Linked Lists

square, odd = lambda x: x * x, lambda x: x % 2 == 1
list(map(square, filter(odd, range(1, 6)))) # [1, 9, 25]

# Try to implement!!!
map_link(square, filter_link(odd, range_link(1, 6))) # Link(1, Link(0, Link(25)))
  • Solution:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def range_link(start, end):
"""Return a Link containing consecutive integers from start to end.

>>> range_link(3, 6)
Link(3, Link(4, Link(5)))
"""
if start >= end:
return Link.empty
else:
return Link(first=start, rest=range_link(start + 1, end))


def map_link(f, s):
"""Return a Link that contains f(x) for each x in Link s.

>>> map_link(square, range_link(3, 6))
Link(9, Link(16, Link(25)))
"""
if s is Link.empty:
return s
elif:
return Link(first=f(s.first), rest=map_link(f, s.rest))


def filter_link(f, s):
"""Return a Link that contains only the elements x of Link s for which f(x)
is a true value.

>>> filter_link(odd, range_link(3, 6))
Link(3, Link(5))
"""
if s is Link.empty:
return s
elif f(s.first):
return Link(first=s.first, rest=filter_link(f, s.rest))
else:
return filter_link(f, s.rest)

Linked List Mutation

Let's see an example.

Tree Class

Tree is another recursive computational data structure.

We've already implemented it in Lecture 12 with data abstraction. Now, let's try to implement it again, but with class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Tree:
"""A tree is a label and a list of branches.

>>> t = Tree(1, [Tree(3), Tree(4)])
>>> print(t)
1
3
4
"""

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

def __repr__(self):
if self.branches:
branch_str = ', ' + repr(self.branches)
else:
branch_str = ''
return 'Tree({0}{1})'.format(repr(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

def is_leaf(self):
return not self.branches

We can use the Tree class easily.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def fib_tree(n):
"""A Fibonacci tree.

>>> print(fib_tree(4))
3
1
0
1
2
1
1
0
1
"""
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])


def leaves(tree):
"""Return the leaf values of a tree.

>>> leaves(fib_tree(4))
[0, 1, 1, 0, 1]
"""
if tree.is_leaf():
return [tree.label]
else:
return sum([leaves(b) for b in tree.branches], [])


def height(tree):
"""The height of a tree."""
if tree.is_leaf():
return 0
else:
return 1 + max([height(b) for b in tree.branches])