If you need help reviewing Iterators and Generators, take a look at these resources:

Each question has a "Toggle Solution" button -- click it to reveal that question's solution.

## Iterators: Cross out the errors

### Question 1

Cross out any incorrect or unnecessary lines. You should not need to write any new lines to make the code work. Do not cross out any doctests.

```
class Naturals:
"""Doctests.
>>> n = Naturals()
>>> i = iter(n)
>>> next(i)
0
>>> next(i)
1
>>> next(i)
2
"""
def __init__(self):
self.cur = 0
def __iter__(self):
def __iter__(self, start):
self.cur = start
while True:
self.cur += 1
return self.cur
return NatIter(self.cur)
def __next__(self):
tmp = self.cur
self.cur += 1
return tmp
class NatIter(Iterator)
class NatIter:
def __init__(self):
def __init__(self, start):
self.cur = start
def __iter__(Self):
return self
def __next__(self):
tmp = self.cur
self.cur += 1
return tmp
```

```
class Naturals:
"""Doctests.
>>> n = Naturals()
>>> i = iter(n)
>>> next(i)
0
>>> next(i)
1
>>> next(i)
2
"""
def __init__(self):
self.cur = 0
def __iter__(self):
return NatIter(self.cur)
class NatIter:
def __init__(self, start):
self.cur = start
def __iter__(Self):
return self
def __next__(self):
tmp = self.cur
self.cur += 1
return tmp
```

## Iterators: Fill in the blanks

### Question 2

Fill in the implementation of the iterator for the Rlist class.

```
class Rlist:
"""Doctests
>>> r = Rlist(1, Rlist(2, Rlist(3, Rlist(4))))
>>> for item in r:
... print(item)
1
2
3
4
"""
class EmptyList:
pass
empty = EmptyList()
def __init__(self, first, rest=empty):
self.first = first
self.rest = rest
self.cur = self
def __iter__(self):
return ______
def __next__(self):
if self.cur == ______:
raise ______
else:
result = ______
______ = self.cur.rest
return result
```

```
class Rlist:
class EmptyList:
pass
empty = EmptyList()
def __init__(self, first, rest=empty):
self.first = first
self.rest = rest
self.cur = self
def __iter__(self):
return self
def __next__(self):
if self.cur == Rlist.empty:
raise StopIteration
else:
result = self.curr.first
self.cur = self.cur.rest
return result
```

Since we are writing a `__next__`

method for the `Rlist`

class,
the `Rlist`

class is technically an iterator. As such, its
`__iter__`

method can just return `self`

. In the
`__next__`

method, if the current Rlist is empty, we must raise a
`StopIteration`

exception. Otherwise, we will return the *element*
(`self.curr.first`

) at the current node, and change our point
(`self.curr`

) to the next node in the Rlist (`self.cur.rest`

)."""

## Generators: Code-writing questions

### Question 3

Write a generator function `zip`

that takes two iterators and yields
elements of thsoe iterators in pairs (see the doctests for
clarification). `zip`

will stop once one of the input iterators stops.

```
def zip(iter1, iter2):
"""Doctests
>>> i1 = iter([1, 2, 3, 4])
>>> i2 = iter([5, 6, 7])
>>> gen = zip(i1, i2)
>>> for elem in gen:
... print(elem)
(1, 5)
(2, 6)
(3, 7)
"""
"*** YOUR CODE HERE ***"
```

```
def zip(iter1, iter2):
while True:
try:
yield (next(iter1), next(iter2))
except StopIteration:
break
```

## Generators: Fill in the blank

### Question 4

Fill in the implementation of `pascals`

, a generator function that
yields successive lines of Pascal's triangle every time `next`

is
called. Each line should be represented as a Python list.

**Hint**: a description of Pascal's triangle can be found
here

```
def pascals():
"""Doctests
>>> p = pascals()
>>> next(p)
[1]
>>> next(p)
[1, 1]
>>> next(p)
[1, 2, 1]
>>> next(p)
[1, 3, 3, 1]
>>> next(p)
[1, 4, 6, 4, 1]
"""
curr = ______
while True:
yield curr
i, new = 1, [1]
while ______:
new.append(______ + ______)
i += 1
new.append(1)
curr = new
```

```
def pascals():
curr = [1]
while True:
yield curr
i, new = 1, [1]
while i < len(curr):
new.append(curr[i-1] + curr[i])
i += 1
new.append(1)
curr = new
```