Solving problem is about exposing yourself to as many situations as possible like How to break out of multiple loops? and practice these strategies over and over. With time, it becomes second nature and a natural way you approach any problems in general. Big or small, always start with a plan, use other strategies mentioned here till you are confident and ready to code the solution.
In this post, my aim is to share an overview the topic about How to break out of multiple loops?, which can be followed any time. Take easy to follow this discuss.
Given the following code (that doesn’t work):
while True: #snip: print out current state while True: ok = get_input("Is this ok? (y/n)") if ok.lower() == "y": break 2 #this doesn't work :( if ok.lower() == "n": break #do more processing with menus and stuff
Is there a way to make this work? Or do I have do one check to break out of the input loop, then another, more limited, check in the outside loop to break out all together if the user is satisfied?
My first instinct would be to refactor the nested loop into a function and use
return to break out.
Here’s another approach that is short. The disadvantage is that you can only break the outer loop, but sometimes it’s exactly what you want.
for a in xrange(10): for b in xrange(20): if something(a, b): # Break the inner loop... break else: # Continue if the inner loop wasn't broken. continue # Inner loop was broken, break the outer. break
This uses the for / else construct explained at: Why does python use ‘else’ after for and while loops?
Key insight: It only seems as if the outer loop always breaks. But if the inner loop doesn’t break, the outer loop won’t either.
continue statement is the magic here. It’s in the for-else clause. By definition that happens if there’s no inner break. In that situation
continue neatly circumvents the outer break.
PEP 3136 proposes labeled break/continue. Guido rejected it because “code so complicated to require this feature is very rare”. The PEP does mention some workarounds, though (such as the exception technique), while Guido feels refactoring to use return will be simpler in most cases.
First, ordinary logic is helpful.
If, for some reason, the terminating conditions can’t be worked out, exceptions are a fall-back plan.
class GetOutOfLoop( Exception ): pass try: done= False while not done: isok= False while not (done or isok): ok = get_input("Is this ok? (y/n)") if ok in ("y", "Y") or ok in ("n", "N") : done= True # probably better raise GetOutOfLoop # other stuff except GetOutOfLoop: pass
For this specific example, an exception may not be necessary.
On other other hand, we often have “Y”, “N” and “Q” options in character-mode applications. For the “Q” option, we want an immediate exit. That’s more exceptional.
I tend to agree that refactoring into a function is usually the best approach for this sort of situation, but for when you really need to break out of nested loops, here’s an interesting variant of the exception-raising approach that @S.Lott described. It uses Python’s
with statement to make the exception raising look a bit nicer. Define a new context manager (you only have to do this once) with:
from contextlib import contextmanager def nested_break(): class NestedBreakException(Exception): pass try: yield NestedBreakException except NestedBreakException: pass
Now you can use this context manager as follows:
with nested_break() as mylabel: while True: print "current state" while True: ok = raw_input("Is this ok? (y/n)") if ok == "y" or ok == "Y": raise mylabel if ok == "n" or ok == "N": break print "more processing"
Advantages: (1) it’s slightly cleaner (no explicit try-except block), and (2) you get a custom-built
Exception subclass for each use of
nested_break; no need to declare your own
Exception subclass each time.
First, you may also consider making the process of getting and validating the input a function; within that function, you can just return the value if its correct, and keep spinning in the while loop if not. This essentially obviates the problem you solved, and can usually be applied in the more general case (breaking out of multiple loops). If you absolutely must keep this structure in your code, and really don’t want to deal with bookkeeping booleans…
You may also use goto in the following way (using an April Fools module from here):
#import the stuff from goto import goto, label while True: #snip: print out current state while True: ok = get_input("Is this ok? (y/n)") if ok == "y" or ok == "Y": goto .breakall if ok == "n" or ok == "N": break #do more processing with menus and stuff label .breakall
I know, I know, “thou shalt not use goto” and all that, but it works well in strange cases like this.
Introduce a new variable that you’ll use as a ‘loop breaker’. First assign something to it(False,0, etc.), and then, inside the outer loop, before you break from it, change the value to something else(True,1,…). Once the loop exits make the ‘parent’ loop check for that value. Let me demonstrate:
breaker = False #our mighty loop exiter! while True: while True: if conditionMet: #insert code here... breaker = True break if breaker: # the interesting part! break # <--- !
If you have an infinite loop, this is the only way out; for other loops execution is really a lot faster. This also works if you have many nested loops. You can exit all, or just a few. Endless possibilities! Hope this helped!
To break out of multiple nested loops, without refactoring into a function, make use of a “simulated goto statement” with the built-in StopIteration exception:
try: for outer in range(100): for inner in range(100): if break_early(): raise StopIteration except StopIteration: pass
See this discussion on the use of goto statements for breaking out of nested loops.