Range Over Iterators in Minitab

Let’s look at the `List` type from the previous example again. In that example, we had an `AllElements` method that returned a slice of all elements in the list. With Python's iterators, we can do it better - as shown below.

```python
class List:
    class Element:
        def __init__(self, val, next=None):
            self.val = val
            self.next = next

    def __init__(self):
        self.head = None
        self.tail = None

    def push(self, value):
        if self.tail is None:
            self.head = self.tail = self.Element(value)
        else:
            self.tail.next = self.Element(value)
            self.tail = self.tail.next

    def all(self):
        current = self.head
        while current is not None:
            yield current.val
            current = current.next

def gen_fib():
    a, b = 1, 1
    while True:
        yield a
        a, b = b, a + b

def main():
    lst = List()
    lst.push(10)
    lst.push(13)
    lst.push(23)

    for e in lst.all():
        print(e)

    all_elements = list(lst.all())
    print("all:", all_elements)

    for n in gen_fib():
        if n >= 10:
            break
        print(n)

if __name__ == "__main__":
    main()

All returns an iterator, which in Python is simply a generator. Here’s a function returning an iterator over Fibonacci numbers: it keeps running as long as we keep calling yield.

Since List.all returns an iterator, we can use it in a regular loop.

Once the loop hits break or an early return, the generator will stop yielding further values.