OOP
Question 4
Write make_max_finder
, which takes in no arguments but returns a function which takes in a list. The function it returns should return the maximum value it’s been called on so far, including the current list and any previous list. You can assume that any list this function takes in will be nonempty and contain only non-negative values.
1 | def make_max_finder(): |
Mutable Trees
8a
Define filter_tree
, which takes in a tree t
and one argument predicate function fn
. It should mutate the tree by removing all branches of any node where calling fn
on its label returns False
. In addition, if this node is not the root of the tree, it should remove that node from the tree as well.
1 | def filter_tree(t, fn): |
8b
Fill in the definition for nth_level_tree_map
, which also takes in a function and a tree,
but mutates the tree by applying the function to every nth
level in the tree, where the root is the 0th level.
1 | def nth_level_tree_map(fn, tree, n): |
Extra Challenge Question 9: Photosynthesis
9a
Fill in the methods below, so that the classes interact correctly according to the
documentation (make sure to keep track of all the counters!).
1 | """ |
9b
Let’s make this a little more realistic by giving these objects ages. Modify the code above to satisfy the following conditions. See the doctest for further guidance.
- Every plant and leaf should have an age, but sugar does not age. Plants have a lifetime of 20 time units, and leaves have a lifetime of 2 time units.
- Time advances by one unit at the end of a call to a plant’s absorb or grow method.
- Every time a leaf dies, it spawns a new leaf on the plant. When a plant dies, its leaf dies, and the plant becomes a zombie plant–no longer subject to time. Zombie plants do not age or die.
- Every time a generation of leaves dies for a zombie plant, twice as many leaves rise from the organic matter of its ancestors–defying scientific explanation.
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71"""
p = Plant()
p.age
0
p.leaves
[|Leaf|]
p.leaves[0].age
0
p.age = 18
p.age
18
p.height
1
p.absorb()
p.materials
[|Sugar|]
p.age
19
p.leaves[0].age
1
p.grow()
p.age
20
p.is_zombie
True
>>>p.leaves
[|Leaf|, |Leaf|]
p.leaves[0].age
0
p.absorb()
p.age
20
"""
You will only need to make changes to the Plant and Leaf classes.
class Plant:
def __init__(self):
"""A Plant has a Leaf, a list of sugars created so far,
and an initial height of 1"""
self.materials = []
self.height = 1
###Write your code here###
def absorb(self):
"""Calls the leaf to create sugar"""
###Write your code here###
def grow(self):
"""A Plant uses all of its sugars,each of which increases
its height by 1"""
for sugar in self.materials:
sugar.activate()
self.height += 1
###Write your code here###
def death(self):
###Write your code here###
class Leaf:
def __init__(self, plant): # plant is a Plant instance
"""A Leaf is initially alive, and keeps track of how many
sugars it has created"""
self.alive = True
self.sugars_used = 0
self.plant = plant
###Write your code here###
def absorb(self):
"""If this Leaf is alive, a Sugar is added to the plant’s
list of sugars"""
if self.alive:
self.plant.materials.append(Sugar(self, self.plant))
###Write your code here###
def death(self):
###Write your code here###
def __repr__(self):
return ‘|Leaf|’Linked list
Question 1
The Link class can represent lists with cycles. That is, a list may contain itself as a sublist.
1 | 1, Link(2, Link(3))) s = Link( |
Implement has_cycle
that returns whether its argument, a Link instance, contains a cycle. There are two ways to do this, both iteratively, either with two pointers or keeping track of Link objects we’ve seen already. Try to come up with both!
1 | def has_cycle(link): |
Solution 2
1 | def has_cycle(link): |
Question 2:
Fill in the following function, which checks to see if a particular sequence of items in one linked list, sub_link
can be found in another linked list link
(the items have to be in order, but not necessarily consecutive).
1 | def seq_in_link(link, sub_link): |
Iterators & Generators
Generator WWPD
1 | >>> def g(n): |
Write a generator function
gen_inf
that returns a generator which yields all the numbers in the provided list one by one in an infinite loop.
1 | 3, 4, 5]) t = gen_inf([ |
1 | def gen_inf(lst): |
Write a function
nested_gen
which, when given a nested list of iterables (including generators) lst
, will return a generator that yields all elements nested within lst in order.
Assume you have already implemented is_iter
, which takes in one argument and returns True if the passed in value is an iterable and False if it is not.
1 | def nested_gen(lst): |
(solution using try / except instead of is_iter:)
1 | def nested_gen(lst): |
Write a function that
when given an iterable lst
, returns a generator object. This generator should iterate over every element of lst
, checking each element to see if it has been changed to a different value from when lst
was originally passed into the generator function. If an element has been changed, the generator should yield it. If the length of lst
is changed to a different value from when it was passed into the function, and next
is called on the generator, the generator should stop iteration.
1 | def mutated_gen(lst): |
Growth
Question 0
What are the runtimes of the following?
1 | def one(n): |
a) $\Theta(1)$ b) $\Theta(log n)$ c) $\Theta(n)$ d) $\Theta(n^2)$ e) $\Theta(2^n)$
a
1 | def two(n): |
a) $\Theta(1)$ b) $\Theta(log n)$ c) $\Theta(n)$ d) $\Theta(n^2)$ e) $\Theta(2^n)$
c
1 | def three(n): |
a) $\Theta(1)$ b) $\Theta(log n)$ c) $\Theta(n)$ d) $\Theta(n^2)$ e) $\Theta(2^n)$
b
1 | def four(n): |
a) $\Theta(1)$ b) $\Theta(log n)$ c) $\Theta(n)$ d) $\Theta(n^2)$ e) $\Theta(2^n)$
d