Due at 11:59:59 pm on Saturday, 04/26/2020.

## Instructions

Download hw11.zip. Inside the archive, you will find starter files for the questions in this homework, along with a copy of the OK autograder.

Submission: When you are done, submit with `python3 ok --submit`. You may submit more than once before the deadline; only the final submission will be scored. Check that you have successfully submitted your code on okpy.org. See this article for more instructions on okpy and submitting assignments.

Readings: This homework relies on following references:

### 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):

def __iter__(self):
return self

def __next__(self):

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):

def __next__(self):

def __iter__(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]
"""

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
"""
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.
``python3 ok -q remainders_generator``