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
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
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/)
> 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
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
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
> 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>
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
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.
However, this is just syntactic suger for a goto.... (ugh :-)
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
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
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! =-----
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
> 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/
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
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
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.
>(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
>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. :)