Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Breaking out of nested loops

1 view
Skip to first unread message

Tal Linzen

unread,
Jan 12, 2002, 6:14:59 AM1/12/02
to
Good day.

One feature I miss in python is the ability to break out of nested loops.
I've searched the newsgroup archives, and found a few threads discussing the
problem, with no apparent conclusion. The two solutions I've seen -- raising
an exception, or putting the loop inside a function and using the return
statement -- are unintuitive (meaning, I didn't come up with them myself
:-) ).

Know if anyone has drafted a PEP for a syntactic solution for this problem?

Tal


Skip Montanaro

unread,
Jan 12, 2002, 9:55:40 AM1/12/02
to

Tal> One feature I miss in python is the ability to break out of nested
Tal> loops.... The two solutions I've seen -- raising an exception, or
Tal> putting the loop inside a function and using the return statement
Tal> -- are unintuitive...

Tal> Know if anyone has drafted a PEP for a syntactic solution for this
Tal> problem?

Well, if there was a PEP, you'd know where to find it:

http://python.sourceforge.net/peps/

:-) (I don't think there is one.)

This topic comes up periodically without a clear resolution. Not as often
as the indentation thing, but more frequently than implicit "self" arguments
to methods. Try googling for "break nested loops" at

http://www.python.org/search/

You might also consider writing a PEP that at least summarizes what you
find. I'd suggest you consider documenting more than just breaking out of
nested loops:

* alternative looping proposals

* named blocks

* inline functions

* breaking out of nested loops

--
Skip Montanaro (sk...@pobox.com - http://www.mojam.com/)

Justin Sheehy

unread,
Jan 12, 2002, 9:50:08 AM1/12/02
to
"Tal Linzen" <pa...@012.net.il> writes:

> One feature I miss in python is the ability to break out of nested loops.
> I've searched the newsgroup archives, and found a few threads discussing the
> problem, with no apparent conclusion. The two solutions I've seen -- raising
> an exception, or putting the loop inside a function and using the return
> statement -- are unintuitive (meaning, I didn't come up with them myself
> :-) ).
>

> Know if anyone has drafted a PEP for a syntactic solution for this problem?

Who needs a syntactic solution now that you've found two very good
ways of doing this that already exist?

Both of the strategies you mention are well-known and very effective.

New syntax is only added when there's a really good justification or
when the new syntax is "print >>".

Further, the print syntax addition is harmless even if it is
unneccesary and ugly, as it doesn't use up a keyword and can't break
any existing working code.

-Justin


Ulf Magnusson

unread,
Jan 12, 2002, 9:55:24 AM1/12/02
to
"Tal Linzen" <pa...@012.net.il> wrote in message
news:mailman.1010834178...@python.org...

I guess you mean something like this
******************
for i in ...:
for j in ....:
if some_condition:
magic_break
code continues here...
******************

There are not supposed to be such a magical break, to my knowledge no
other language support this either. It kind of breaks the good behaviour you
expect from a imparative programming language.

The best solution is to set a flag inside the inner for loop and check for
that
the first thing in the outer for loop. Exceptions should only be used for
real exceptions when something is wrong, not to change the flow of the
program.

But this is of course my humble opinion

Do I make any sence?

/U. Magnusson


Peter Hansen

unread,
Jan 12, 2002, 11:51:02 AM1/12/02
to
Ulf Magnusson wrote:
>
> I guess you mean something like this
> ******************
> for i in ...:
> for j in ....:
> if some_condition:
> magic_break
> code continues here...
> ******************
>
> There are not supposed to be such a magical break, to my knowledge no
> other language support this either.

Sure they do. 'goto', probably among others.

> It kind of breaks the good behaviour you
> expect from a imparative programming language.

That much is true.

> The best solution is to set a flag inside the inner for loop and check for
> that the first thing in the outer for loop.

You have to check that flag in each loop or you won't get yourself
out to the outer loop to have it checked. That tends to make the
code somewhat unreadable when you have more than two nested loops.
One could argue that code with three nested loops already smells
a bit and should be refactored, but that's another story.

> Exceptions should only be used for real exceptions when something
> is wrong, not to change the flow of the program.

The word "exception" simply refers to something other than the
normal case. There's really nothing wrong with using it for
*exceptional* conditions, which obviously is the case with most
magic_breaks that get you out of a deeply nested loop. There
are sometimes concerns with performance (using exceptions can
be much slower than simpler things in some languages) but
generally I think this is probably the best way.

> But this is of course my humble opinion
>
> Do I make any sence?

This is just _my_ humble opinion, but I think we both make sense... :)

-Peter

Chris Liechti

unread,
Jan 12, 2002, 11:57:13 AM1/12/02
to
"Ulf Magnusson" <ulf.ma...@ubm-computing.com> wrote in
news:w7Y%7.12719$l93.2...@newsb.telia.net:

> I guess you mean something like this
> ******************
> for i in ...:
> for j in ....:
> if some_condition:
> magic_break
> code continues here...
> ******************
>
> There are not supposed to be such a magical break, to my knowledge no
> other language support this either. It kind of breaks the good
> behaviour you expect from a imparative programming language.

Java does support this!

loop1:
for(;;) {
for(;;) {
...
if (...) break loop1;
}
}

(the labels only work with break statements there is no goto in java.)

--
Chris <clie...@gmx.net>

Dietmar Schwertberger

unread,
Jan 12, 2002, 1:57:22 PM1/12/02
to
In article <mailman.101084713...@python.org>, Justin Sheehy

IMHO "break n" (default n=1) wouldn't be ugly, at least less ugly than
the workarounds...


Example:

for x in range(10):
for y in range(10):
for z in range(10):
if abc(x,y,z):
break 3

Regards,

Dietmar

Peter Hansen

unread,
Jan 12, 2002, 3:38:14 PM1/12/02
to
Dietmar Schwertberger wrote:
>
> IMHO "break n" (default n=1) wouldn't be ugly, at least less ugly than
> the workarounds...
>
> Example:
>
> for x in range(10):
> for y in range(10):
> for z in range(10):
> if abc(x,y,z):
> break 3

But it's not maintainable. Someone refactoring the code to insert another
loop might not notice or deal with the break properly. At least a label
has the benefit of being an absolute target.

Ulf Magnusson

unread,
Jan 12, 2002, 3:44:27 PM1/12/02
to
"Chris Liechti" <clie...@gmx.net> wrote in message
news:Xns9194B718F5FA...@62.2.16.82...

However, this is just syntactic suger for a goto.... (ugh :-)

Ulf Magnusson

unread,
Jan 12, 2002, 3:56:01 PM1/12/02
to
"Peter Hansen" <pe...@engcorp.com> wrote in message
news:3C406976...@engcorp.com...

> Ulf Magnusson wrote:
> >
> > I guess you mean something like this
> > ******************
> > for i in ...:
> > for j in ....:
> > if some_condition:
> > magic_break
> > code continues here...
> > ******************
> >
> > There are not supposed to be such a magical break, to my knowledge no
> > other language support this either.
>
> Sure they do. 'goto', probably among others.

I actually thought about that before, but goto in an ameoba which
shouldn't exist in any programming language except assembler.
that of course also IMHO.
But the fact remains, it could be used to perform the wished behaviour,
which you pointed you.

> > It kind of breaks the good behaviour you
> > expect from a imparative programming language.
>
> That much is true.
>
> > The best solution is to set a flag inside the inner for loop and check
for
> > that the first thing in the outer for loop.
>
> You have to check that flag in each loop or you won't get yourself
> out to the outer loop to have it checked. That tends to make the
> code somewhat unreadable when you have more than two nested loops.
> One could argue that code with three nested loops already smells
> a bit and should be refactored, but that's another story.
>
> > Exceptions should only be used for real exceptions when something
> > is wrong, not to change the flow of the program.
>
> The word "exception" simply refers to something other than the
> normal case. There's really nothing wrong with using it for
> *exceptional* conditions, which obviously is the case with most
> magic_breaks that get you out of a deeply nested loop. There
> are sometimes concerns with performance (using exceptions can
> be much slower than simpler things in some languages) but
> generally I think this is probably the best way.
>
> > But this is of course my humble opinion
> >
> > Do I make any sence?
>
> This is just _my_ humble opinion, but I think we both make sense... :)

Yes, I agree with you Peter, but I guess it is more a question of religion
than
what is right or wrong here.
I for one, am really restrictive using exceptions in non "failure"
situations
>
> -Peter

/U. Magnusson


Steven Majewski

unread,
Jan 12, 2002, 8:18:19 PM1/12/02
to

On Sat, 12 Jan 2002, Ulf Magnusson wrote:

> "Peter Hansen" <pe...@engcorp.com> wrote in message
> >

> > > Exceptions should only be used for real exceptions when something
> > > is wrong, not to change the flow of the program.
> >
> > The word "exception" simply refers to something other than the
> > normal case. There's really nothing wrong with using it for
> > *exceptional* conditions, which obviously is the case with most
> > magic_breaks that get you out of a deeply nested loop. There
> > are sometimes concerns with performance (using exceptions can
> > be much slower than simpler things in some languages) but
> > generally I think this is probably the best way.
> >
> > > But this is of course my humble opinion
> > >
> > > Do I make any sence?
> >
> > This is just _my_ humble opinion, but I think we both make sense... :)
>
> Yes, I agree with you Peter, but I guess it is more a question of religion
> than
> what is right or wrong here.
> I for one, am really restrictive using exceptions in non "failure"
> situations

I don't think it's a matter of religion. It's a matter of language.
In some other languages, 'non failure' mode exceptions may be unusual,
but it's the normal idiom in Python. This has been discussed before
several times. It's not a matter of opinion: Peter's objectively
correct. Python's for loop implementation has used exceptions to
break out of the loop. The exact implementation has changed with
the addition of iterators, but before StopIteration, it was another
exception. You might object that that usage is 'under the hood' ,
however, there are cases where you may likely use an explicit
'raise StopIteration' in your code to interact with that 'under
the hood' protocol.
Part of the idea behind Exceptions as classes was to make it
easier to classify different sorts of exceptions: failure exceptions
vs. control exceptions for one example.

However, after trying out an example with multilevel nesting,
I see that, although it works fine, it does appear a bit awkward
in Python:

# --------------------------
class Break1(Exception): pass
class Break2(Exception): pass
class Break3(Exception): pass

from random import randint
tab = ' '

def btest():
try:
while 1:
print 'While1'
try: ##
while 2:
print tab,'While2'
try:
while 3:
print tab*2,'While3'
for i in range(30):
r = randint(0,6)
print tab*3,'r=',r
if r == 1: raise Break1
elif r == 2: raise Break2
elif r == 3: raise Break3
except Break3:
print tab*2,Break3
except Break2:
print tab,Break2
except Break1:
print Break1

for test in (1,2,3):
print 'test',test
btest()
#-------------------------

I admit, it's not simple to follow the control thru all of the
extra try/excepts and indentation. However, since the exceptions
do work across function boundaries, if you break it up into
nested functions (that then still raise exceptions) it's easier
to follow.

But then, I'm not sure that adding something like named blocks would
be any more readable. You would still need an extra level of nesting,
but the lack of an 'except' would make it more difficult to spot
the point it breaks out to. And having to count levels for a syntax
like "break 3" would be even worse.

( Although, I find named blocks in Lisp OK, but you're more free
to use the indentation to make the flow more clear. )

That conclusion tends to reinforce my initial reaction to these
proposals: that the 'problem' is really that deeply nested loops
in a single procedure is the real 'design error' , not any particular
syntax. Sometimes it can't be avoided, but I haven't seen a proposal
for new syntax that is any easier to read than the example above.


-- Steve Majewski

Joshua Muskovitz

unread,
Jan 12, 2002, 10:21:35 PM1/12/02
to
"Dietmar Schwertberger" <die...@schwertberger.de> wrote in message
news:ant1218220b02%2...@schwertberger.freenet.de...

> IMHO "break n" (default n=1) wouldn't be ugly, at least less ugly than
> the workarounds...
> Example:
> for x in range(10):
> for y in range(10):
> for z in range(10):
> if abc(x,y,z):
> break 3

I vote that we should instead implement the INTERCAL statement "returnfrom".
That way, it won't be affected by refactoring. :-)

More seriously though, I would say either implement a flag to drop out of
the loop (when possible), use exceptions (your own custom ones, to avoid
inadvertantly catching something you weren't supposed to), or restructure
the code to avoid the situation.

-- josh


-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 80,000 Newsgroups - 16 Different Servers! =-----

Bengt Richter

unread,
Jan 13, 2002, 12:53:31 AM1/13/02
to
On Sat, 12 Jan 2002 20:18:19 -0500 (EST), Steven Majewski <sd...@Virginia.EDU> wrote:
[...]
How about using a dedent-level marker on the next line after the break,
e.g., a lone ':' ? Then the eye can just drop down to where it's going.

def btest():
while 1:
print 'While1'


while 2:
print tab,'While2'

while 3:
print tab*2,'While3'
for i in range(30):
r = randint(0,6)
print tab*3,'r=',r

if r == 1: break
:
elif r == 2: break
:
elif r == 3: break
:
print tab*2,Break3
print tab,Break2

Paul Sidorsky

unread,
Jan 13, 2002, 2:20:32 AM1/13/02
to
Steven Majewski wrote:

> That conclusion tends to reinforce my initial reaction to these
> proposals: that the 'problem' is really that deeply nested loops
> in a single procedure is the real 'design error' , not any particular
> syntax. Sometimes it can't be avoided, but I haven't seen a proposal
> for new syntax that is any easier to read than the example above.

I tend to agree that this feature isn't really needed - the flag
approach or isolate-into-a-function approach usually works for me.
However, such a feature might be reasonable if the "strong" break is
tied to to the loop itself rather than the break statement. For
example, something like this:

i = 1
while i < 20:
j = 1
while trans j < 20:
if i * j > 200:
break # Quits both loops because of trans
j += 1
i += 1

Here "trans" is a new keyword or directive that tells Python that a
break in the loop is to be "transparent", i.e. that it will propagate
down to the loop below. ("yield" could even be used for this purpose as
it sort of makes sense, but that's a pretty gross abuse of the
keyword!) These could of course be nested to allow multiple-level
break-outs, and intermixed with non-"trans" loops as needed.

To me, the main advantages with this approach are:

1) 100% backwards compatible. (It should also work with for loops too.)

2) Explicit - you can tell immediately from looking at the loop
construct what will happen. You also are stuck with "transparent"
breaks if you choose them, so if you want something more complicated you
have to find a better solution (which, as Steven mentioned, is probably
a good idea anyhow).

3) Maintainable, at least more so than some of the other solutions.
There's no need to count indentation levels or update labels. When
adding/removing a loop the only consideration is whether to update the
"transparentness" of other loops in the nest.

Anyhow, I'm really just thinking out loud. Like I said, I won't
complain if such a feature is never added.

P.S. I keep putting "transparent" in quotes because it doesn't seem to
be the best word for this type of behaviour, but I can't think of a
better one right now.

--
======================================================================
Paul Sidorsky Calgary, Canada
pau...@shaw.ca http://members.shaw.ca/paulsid/

Jonathan Gardner

unread,
Jan 13, 2002, 3:49:04 AM1/13/02
to
> > There are not supposed to be such a magical break, to my knowledge no
> > other language support this either. It kind of breaks the good
> > behaviour you expect from a imparative programming language.
>
> Java does support this!
>

Perl has this:

LOOP_A: for ... {
LOOP_B: while .. {
next LOOP_A if some condition;
}
}

It was very easy to understand for me, and I think it made the code a lot
easier to write and think about. I've used it several times. I think the
other solutions (flags or exceptions) are a bit too cryptic.

Jonathan

Alex Martelli

unread,
Jan 13, 2002, 11:38:12 AM1/13/02
to
Justin Sheehy wrote:
...

> New syntax is only added when there's a really good justification or
> when the new syntax is "print >>".

Good summary.

> Further, the print syntax addition is harmless even if it is
> unneccesary and ugly, as it doesn't use up a keyword and can't break
> any existing working code.

Bad summation. By this reasoning, we should have "break >> 3" -- it's
unnecessary, ugly, doesn't use up a keyword, can't break any existing
working code.


Alex

Peter Hansen

unread,
Jan 13, 2002, 12:31:36 PM1/13/02
to
Paul Sidorsky wrote:
>
> I tend to agree that this feature isn't really needed - the flag
> approach or isolate-into-a-function approach usually works for me.
> However, such a feature might be reasonable if the "strong" break is
> tied to to the loop itself rather than the break statement. For
> example, something like this:
>
> i = 1
> while i < 20:
> j = 1
> while trans j < 20:
> if i * j > 200:
> break # Quits both loops because of trans
> j += 1
> i += 1
>
> Here "trans" is a new keyword or directive that tells Python that a
> break in the loop is to be "transparent", i.e. that it will propagate
> down to the loop below.

Not very flexible though, as you can't have a break in the inner
loop break *only* out of the inner loop, and yet have another
break in the next-to-inner loop break out of that loop, and still
have your "trans" break manage to get out of all the loops.
That is, this won't work:

while #1
while trans #2
while trans #3
break (normally) to the middle while
break (trans-style) to outer loop
break normally (to outer loop)

> To me, the main advantages with this approach are:
>
> 1) 100% backwards compatible.

> 2) Explicit

I'm not sure it's very explicit, since you are affecting the
behaviour of *break* statements, yet applying the modifier
to the loop construct.

> 3) Maintainable

Not very maintainable if not readable. No other language that
I'm aware of has such a thing, and that makes it a somewhat
bizarre new concept that nobody would "get" at first.

More important that anything when considering a new syntax,
however, is the question do you *really* need it.
No! As you said, we don't.

Michael Kelly

unread,
Jan 13, 2002, 3:45:34 PM1/13/02
to
On 12 Jan 2002 17:57:13 +0100, Chris Liechti <clie...@gmx.net> wrote:

>(the labels only work with break statements there is no goto in java.)

So what it amounts to is a crippled goto. That's reassuring! :-)


Mike

--

"I don't want to belong to any club that would have me as a member."
-- Groucho Marx

Michael Kelly

unread,
Jan 13, 2002, 3:53:24 PM1/13/02
to
On Sun, 13 Jan 2002 17:49:04 +0900, Jonathan Gardner
<jga...@alumni.washington.edu> wrote:

>It was very easy to understand for me, and I think it made the code a lot
>easier to write and think about. I've used it several times. I think the
>other solutions (flags or exceptions) are a bit too cryptic.

A non-cryptic idiom in Perl! I'll have to mark it down for posterity!
:)

I get a kick out of the "goto must die" mentality sometimes.
Surely just jumping all around in code makes it into a pile
of spaghetti and I'm aware of the "flushing the pipeline"
issues etc.. but if used sparingly a "goto" can be cleaner
esp. when coming out of nested loops. You might have
some nested loops nested a little more than usual say
when you eliminate recursion by using another enclosing
loop. Also the "flush the pipeline" arguments often only
take into consideration the source code look of the language
not realizing that many of these smooth-looking language
control structures devolve into a compare and jump when
disassembled anyway. So a smart compiler may negate
the comparison to optimize when the jump is or isn't taken
depending on the processor architecture.

There's only so much you can do at the source code level
IOW. :)

0 new messages