Iterators and Generators: exam-level questions

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