# Homework 11 Solutions

**Solutions:** You can find the file with solutions for all
questions here.

### Question 1: Scale

Implement an iterator class called `ScaleIterator`

that scales elements in an
iterable `s`

by a number `k`

.

```
class ScaleIterator:
"""An iterator the scales elements of the iterable s by a number k.
>>> s = ScaleIterator([1, 5, 2], 5)
>>> list(s)
[5, 25, 10]
>>> m = ScaleIterator(naturals(), 2)
>>> [next(m) for _ in range(5)]
[2, 4, 6, 8, 10]
"""
def __init__(self, s, k):
self.s = iter(s)
self.k = k
def __iter__(self):
return self
def __next__(self):
return next(self.s) * self.k
```

Use OK to test your code:

`python3 ok -q ScaleIterator`

### Question 2: Restart

Implement an iterator class called `IteratorRestart`

that will reset to the beginning, unlike a normal Iterator that would throw a StopIteration Exception at the end.

```
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`

### Question 3: Scale

Implement the generator function `scale(s, k)`

, which yields elements of the
given iterable `s`

, scaled by `k`

.

```
def scale(s, k):
"""Yield elements of the iterable s scaled by a number k.
>>> s = scale([1, 5, 2], 5)
>>> type(s)
<class 'generator'>
>>> list(s)
[5, 25, 10]
>>> m = scale(naturals(), 2)
>>> [next(m) for _ in range(5)]
[2, 4, 6, 8, 10]
"""
for elem in s:
yield elem * k
```

Use OK to test your code:

`python3 ok -q scale`

### Question 4: Remainder generator

Like functions, generators can also be *higher-order*. For this
problem, we will be writing `remainders_generator`

, which yields a
series of generator objects.

`remainders_generator`

takes in an integer `m`

, and yields `m`

different
generators. The first generator is a generator of multiples of `m`

, i.e. numbers
where the remainder is 0. The second, a generator of natural numbers with
remainder 1 when divided by `m`

. The last generator yield natural numbers with
remainder `m - 1`

when divided by `m`

.

```
def remainders_generator(m):
"""
Takes in an integer m, and yields m different remainder groups
of m.
>>> remainders_mod_four = remainders_generator(4)
>>> for rem_group in remainders_mod_four:
... for _ in range(3):
... print(next(rem_group))
0
4
8
1
5
9
2
6
10
3
7
11
"""
def remainder_group(rem):
start = rem
while True:
yield start
start += m
for rem in range(m):
yield remainder_group(rem)
```

Note that if you have implemented this correctly, each of the
generators yielded by `remainder_generator`

will be *infinite* - you
can keep calling `next`

on them forever without running into a
`StopIteration`

exception.

**Hint**: Consider defining an inner generator function. What arguments
should it take in? Where should you call it?

Use OK to test your code:

`python3 ok -q remainders_generator`

### Question 5: Project

Nothing to submit for this question, just a reminder to work on your project! The checkpoint is due April 22nd! Good luck!