Homework 10 Solutions
Solution Files
You can find the solutions in hw10.py.
Required Questions
Iterators and Generators
Q1: Restart
Use Ok to test your knowledge with the following What would Python display questions:
python3 ok -q restart -u
>>> class IteratorA:
... def __init__(self):
... self.start = 10
... def __next__(self):
... if self.start > 100:
... raise StopIteration
... self.start += 20
... return self.start
... def __iter__(self):
... return self
>>> iterator = IteratorA()
>>> [num for num in iterator]
[30, 50, 70, 90, 110]
>>> [num for num in iterator]
[]
>>> class IteratorB:
... def __init__(self):
... self.start = -6
... def __next__(self):
... if self.start > 10:
... raise StopIteration
... self.start += 3
... return self.start
... def __iter__(self):
... return self
>>> iterator = IteratorB()
>>> [num for num in iterator]
[-3, 0, 3, 6, 9, 12]
>>> [num for num in iterator]
[]
The outputs of the list comprehensions are like that because the instance
variables are not reset each time a for loop is started. Therefore, when a
StopIteration exception is raised at the end of the first list comprehension,
it will be raised immediately at the beginning of the second. With that in mind,
try writing an iterator that "restarts" every time it is run through a for
loop.
class IteratorRestart:
"""
>>> iterator = IteratorRestart(2, 7)
>>> for num in iterator:
... print(num)
2
3
4
5
6
7
>>> for num in iterator:
... print(num)
2
3
4
5
6
7
"""
def __init__(self, start, end):
self.start = start
self.end = end
self.current = start
def __next__(self):
if self.current > self.end:
raise StopIteration
self.current += 1
return self.current - 1
def __iter__(self):
self.current = self.start
return self
Use Ok to test your code:
python3 ok -q IteratorRestart
Q2: Amplify
Implement amplify, a generator function that takes a one-argument function f and a starting value x. The element at index k that it yields (starting at 0) is the result of applying f k times to x. It terminates whenever the next value it would yield is a falsy value, such as 0, "", [], False, etc.
def amplify(f, x):
"""Yield the values x, f(x), f(f(x)), ... that are all truthy values
and stop yielding once the sequence reaches the first falsy value.
>>> gen1 = amplify(lambda s: s[1:], 'boxes')
>>> [next(gen1) for _ in range(5)]
['boxes', 'oxes', 'xes', 'es', 's']
>>> try:
... next(gen1)
... except StopIteration:
... print('Correctly raised StopIteration')
... else:
... print('Expected StopIteration error but was not raised')
Correctly raised StopIteration
>>> gen2 = amplify(lambda x: x // 2 - 1, 14)
>>> [next(gen2) for _ in range(3)]
[14, 6, 2]
>>> try:
... next(gen2)
... except StopIteration:
... print('Correctly raised StopIteration')
... else:
... print('Expected StopIteration error but was not raised')
Correctly raised StopIteration
"""
while x:
yield x
x = f(x)
Use Ok to test your code:
python3 ok -q amplify
Q3: Countdown
Write both a generator function and an iterator (that is not a generator) that count down to 0.
def countdown(n):
"""
A generator that counts down from N to 0.
>>> for number in countdown(5):
... print(number)
...
5
4
3
2
1
0
>>> for number in countdown(2):
... print(number)
...
2
1
0
"""
while n >= 0:
yield n
n = n - 1
class Countdown:
"""
An iterator that counts down from N to 0.
>>> for number in Countdown(5):
... print(number)
...
5
4
3
2
1
0
>>> for number in Countdown(2):
... print(number)
...
2
1
0
"""
def __init__(self, cur):
self.cur = cur
def __next__(self):
if self.cur < 0:
raise StopIteration
self.cur -= 1
return self.cur + 1
def __iter__(self):
"""So that we can use this iterator as an iterable."""
return self
Use Ok to test your code:
python3 ok -q countdown
python3 ok -q Countdown
Submit Assignment
Submit this assignment by uploading any files you've edited to the appropriate Gradescope assignment. Lab 00 has detailed instructions.