fun with nested loops

Daniel dalist0 at gmail.com
Fri Sep 2 02:21:33 CEST 2011


I thought a bit about Carl's and Thomas' proposals, and it gave me an
idea how this problem could be approached:
Break is relatively easy to implement with a context manager that
returns an iterable that throws an exception specific to that context
manager:

with named_loop(i for i in range(10)) as loop1:
    for i in loop1:
        with named_loop(i for i in range(10)) as loop2a:
            for j in loop2a:
                loop1._break() # this is easy
                loop1._continue() # this is difficult
                # if we _continue here, we need to do a continue right
after the with loop2a:
        if loop1.cont: continue # context manager does not create new
scope

        with named_loop(i for i in range(10)) as loop2b:
            for j in loop2b:
                loop1._break()

this throws an exception that propagates through all the context
managers till it hits the one that made loop1
at that point the exception is caught.

Now using the idea of break_levels, something like loop1._continue()
should work.
It is more difficult, because it should be caught in the last loop
before the one that is targeted,
loop1._continue throws an exception that is caught in loop2. Then
loop1 just continues with the next value.

I don't know how loop2 can learn that it is enclosed in loop1. Maybe
loop1 could add itself to a global stack on enter
and delete itself on exit, or maybe inspect could help?

The big problem is that loop1._continue breaks out of loop2a, but then
starts to execute loop2b, which we don't want.
If loop1 is _continued inside of loop2a, a continue needs to directly
follow the loop2a with block.

An alternative would be to wrap each sequence of statements in another
with statement, I think this is better:

for i in loop1:
    with sequenced_stuff():
        with named_loop(i for i in range(10)) as loop2a:
            for j in loop2a:
                loop1._continue() # this is caught in
sequenced_stuff()

        with named_loop(i for i in range(10)) as loop2b:
            for j in loop2b:
                loop1._break()


Loops can even be restarted with a small modification:
In a loop like "for i in loop1:" the __iter__ method of loop1 is
called. If __iter__ returns a smart iterator that keeps a reference to
loop1, then it can be restarted, advanced etc.
loop1.restart() would throw an exception that __exits__ all the inner
loops and gets caught in the loop just before loop1. Then it resets
the iterable that the iterator returned by __iter__ links to, i.e.
loop1 restarts.
Nice side benefit: loops can have a method peek(n) to look ahead.

Thanks for all your input,

Dan






More information about the Python-list mailing list