How to break out of multiple loops?

若如初见. 提交于 2019-11-26 03:13:35

问题


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?


回答1:


My first instinct would be to refactor the nested loop into a function and use return to break out.




回答2:


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.

The 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.




回答3:


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.




回答4:


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.




回答5:


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
@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.




回答6:


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.




回答7:


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!




回答8:


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.




回答9:



keeplooping=True
while keeplooping:
    #Do Stuff
    while keeplooping:
          #do some other stuff
          if finisheddoingstuff(): keeplooping=False

or something like that. You could set a variable in the inner loop, and check it in the outer loop immediately after the inner loop exits, breaking if appropriate. I kinda like the GOTO method, provided you don't mind using an April Fool's joke module - its not Pythonic, but it does make sense.




回答10:


This isn't the prettiest way to do it, but in my opinion, it's the best way.

def loop():
    while True:
    #snip: print out current state
        while True:
            ok = get_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": return
            if ok == "n" or ok == "N": break
        #do more processing with menus and stuff

I'm pretty sure you could work out something using recursion here as well, but I dunno if that's a good option for you.




回答11:


And why not to keep looping if two conditions are true? I think this is a more pythonic way:

dejaVu = True

while dejaVu:
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y" or ok == "n" or ok == "N":
            dejaVu = False
            break

Isn't it?

All the best.




回答12:


Factor your loop logic into an iterator that yields the loop variables and returns when done -- here is a simple one that lays out images in rows/columns until we're out of images or out of places to put them:

def it(rows, cols, images):
    i = 0
    for r in xrange(rows):
        for c in xrange(cols):
            if i >= len(images):
                return
            yield r, c, images[i]
            i += 1 

for r, c, image in it(rows=4, cols=4, images=['a.jpg', 'b.jpg', 'c.jpg']):
    ... do something with r, c, image ...

This has the advantage of splitting up the complicated loop logic and the processing...




回答13:


In this case, as pointed out by others as well, functional decomposition is the way to go. Code in Python 3:

def user_confirms():
    while True:
        answer = input("Is this OK? (y/n) ").strip().lower()
        if answer in "yn":
            return answer == "y"

def main():
    while True:
        # do stuff
        if user_confirms():
            break



回答14:


There is a hidden trick in the Python while ... else structure which can be used to simulate the double break without much code changes/additions. In essence if the while condition is false, the else block is triggered. Neither exceptions, continue or break trigger the else block. For more information see answers to "Else clause on Python while statement", or Python doc on while (v2.7).

while True:
    #snip: print out current state
    ok = ""
    while ok != "y" and ok != "n":
        ok = get_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N":
            break    # Breaks out of inner loop, skipping else

    else:
        break        # Breaks out of outer loop

    #do more processing with menus and stuff

The only downside is that you need to move the double breaking condition into the while condition (or add a flag variable). Variations of this exists also for the for loop, where the else block is triggered after loop completion.




回答15:


Another way of reducing your iteration to a single-level loop would be via the use of generators as also specified in the python reference

for i, j in ((i, j) for i in A for j in B):
    print(i , j)
    if (some_condition):
        break

You could scale it up to any number of levels for the loop

The downside is that you can no longer break only a single level. It's all or nothing.

Another downside is that it doesn't work with a while loop. I originally wanted to post this answer on Python - `break` out of all loops but unfortunately that's closed as a duplicate of this one




回答16:


My reason for coming here is that i had an outer loop and an inner loop like so:

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

  do some other stuff with x

As you can see, it won't actually go to the next x, but will go to the next y instead.

what i found to solve this simply was to run through the array twice instead:

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

for x in array:
  do some other stuff with x

I know this was a specific case of OP's question, but I am posting it in the hope that it will help someone think about their problem differently while keeping things simple.




回答17:


Try using an infinite generator.

from itertools import repeat
inputs = (get_input("Is this ok? (y/n)") for _ in repeat(None))
response = (i.lower()=="y" for i in inputs if i.lower() in ("y", "n"))

while True:
    #snip: print out current state
    if next(response):
        break
    #do more processing with menus and stuff



回答18:


By using a function:

def myloop():
    for i in range(1,6,1):  # 1st loop
        print('i:',i)
        for j in range(1,11,2):  # 2nd loop
            print('   i, j:' ,i, j)
            for k in range(1,21,4):  # 3rd loop
                print('      i,j,k:', i,j,k)
                if i%3==0 and j%3==0 and k%3==0:
                    return  # getting out of all loops

myloop()

Try running the above codes by commenting out the return as well.

Without using any function:

done = False
for i in range(1,6,1):  # 1st loop
    print('i:', i)
    for j in range(1,11,2):  # 2nd loop
        print('   i, j:' ,i, j)
        for k in range(1,21,4):  # 3rd loop
            print('      i,j,k:', i,j,k)
            if i%3==0 and j%3==0 and k%3==0:
                done = True
                break  # breaking from 3rd loop
        if done: break # breaking from 2nd loop
    if done: break     # breaking from 1st loop

Now, run the above codes as is first and then try running by commenting out each line containing break one at a time from the bottom.




回答19:


An easy way to turn multiple loops into a single, breakable loop is to use numpy.ndindex

for i in range(n):
  for j in range(n):
    val = x[i, j]
    break # still inside the outer loop!

for i, j in np.ndindex(n, n):
  val = x[i, j]
  break # you left the only loop there was!

You do have to index into your objects, as opposed to being able to iterate through the values explicitly, but at least in simple cases it seems to be approximately 2-20 times simpler than most of the answers suggested.




回答20:


# this version uses a level counter to choose how far to break out

break_levels = 0
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_levels = 1        # how far nested, excluding this break
            break
        if ok == "n" or ok == "N":
            break                   # normal break
    if break_levels:
        break_levels -= 1
        break                       # pop another level
if break_levels:
    break_levels -= 1
    break

# ...and so on



回答21:


probably little trick like below will do if not prefer to refactorial into function

added 1 break_level variable to control the while loop condition

break_level = 0
# while break_level < 3: # if we have another level of nested loop here
while break_level < 2:
    #snip: print out current state
    while break_level < 1:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": break_level = 2 # break 2 level
        if ok == "n" or ok == "N": break_level = 1 # break 1 level



回答22:


You can define a variable( for example break_statement ), then change it to a different value when two-break condition occurs and use it in if statement to break from second loop also.

while True:
    break_statement=0
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N": 
            break
        if ok == "y" or ok == "Y": 
            break_statement=1
            break
    if break_statement==1:
        break



回答23:


Solutions in 2 Ways

With an example: Are these two matrices equal/same?
matrix1 and matrix2 are same size, n, 2 dimentional matrices.

First Solution, without a function

same_matrices = True
inner_loop_broken_once = False
n = len(matrix1)

for i in range(n):
    for j in range(n):

        if matrix1[i][j] != matrix2[i][j]:
            same_matrices = False
            inner_loop_broken_once = True
            break

    if inner_loop_broken_once:
        break

Second Solution, with a function
This is the final solution for my case

def are_two_matrices_the_same (matrix1, matrix2):
    n = len(matrix1)
    for i in range(n):
        for j in range(n):
            if matrix1[i][j] != matrix2[i][j]:
                return False
    return True

Have a nice day!




回答24:


# this version breaks up to a certain label

break_label = None
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_label = "outer"   # specify label to break to
            break
        if ok == "n" or ok == "N":
            break
    if break_label:
        if break_label != "inner":
            break                   # propagate up
        break_label = None          # we have arrived!
if break_label:
    if break_label != "outer":
        break                       # propagate up
    break_label = None              # we have arrived!

#do more processing with menus and stuff



回答25:


I'd like to remind you that functions in Python can be created right in the middle of the code and can access the surrounding variables transparently for reading and with nonlocal or global declaration for writing.

So you can use a function as a "breakable control structure", defining a place you want to return to:

def is_prime(number):

    foo = bar = number

    def return_here():
        nonlocal foo, bar
        init_bar = bar
        while foo > 0:
            bar = init_bar
            while bar >= foo:
                if foo*bar == number:
                    return
                bar -= 1
            foo -= 1

    return_here()

    if foo == 1:
        print(number, 'is prime')
    else:
        print(number, '=', bar, '*', foo)

>>> is_prime(67)
67 is prime
>>> is_prime(117)
117 = 13 * 9
>>> is_prime(16)
16 = 4 * 4



回答26:


Hopefully this helps:

x = True
y = True
while x == True:
    while y == True:
         ok = get_input("Is this ok? (y/n)") 
         if ok == "y" or ok == "Y":
             x,y = False,False #breaks from both loops
         if ok == "n" or ok == "N": 
             break #breaks from just one



回答27:


Similar like the one before, but more compact. (Booleans are just numbers)

breaker = False #our mighty loop exiter!
while True:
    while True:
        ok = get_input("Is this ok? (y/n)")
        breaker+= (ok.lower() == "y")
        break

    if breaker: # the interesting part!
        break   # <--- !



回答28:


Since this question has become a standard question for breaking into a particular loop, I would like to give my answer with example using Exception.

Although there exists no label named breaking of loop in multipally looped construct, we can make use of User-defined Exceptions to break into a particular loop of our choice. Consider the following example where let us print all numbers upto 4 digits in base-6 numbering system:

class BreakLoop(Exception):
    def __init__(self, counter):
        Exception.__init__(self, 'Exception 1')
        self.counter = counter

for counter1 in range(6):   # Make it 1000
    try:
        thousand = counter1 * 1000
        for counter2 in range(6):  # Make it 100
            try:
                hundred = counter2 * 100
                for counter3 in range(6): # Make it 10
                    try:
                        ten = counter3 * 10
                        for counter4 in range(6):
                            try:
                                unit = counter4
                                value = thousand + hundred + ten + unit
                                if unit == 4 :
                                    raise BreakLoop(4) # Don't break from loop
                                if ten == 30: 
                                    raise BreakLoop(3) # Break into loop 3
                                if hundred == 500:
                                    raise BreakLoop(2) # Break into loop 2
                                if thousand == 2000:
                                    raise BreakLoop(1) # Break into loop 1

                                print('{:04d}'.format(value))
                            except BreakLoop as bl:
                                if bl.counter != 4:
                                    raise bl
                    except BreakLoop as bl:
                        if bl.counter != 3:
                            raise bl
            except BreakLoop as bl:
                if bl.counter != 2:
                    raise bl
    except BreakLoop as bl:
        pass

When we print the output, we will never get any value whose unit place is with 4. In that case, we don't break from any loop as BreakLoop(4) is raised and caught in same loop. Similarly, whenever ten place is having 3, we break into third loop using BreakLoop(3). Whenever hundred place is having 5, we break into second loop using BreakLoop(2) and whenver the thousand place is having 2, we break into first loop using BreakLoop(1).

In short, raise your Exception (in-built or user defined) in the inner loops, and catch it in the loop from where you want to resume your control to. If you want to break from all loops, catch the Exception outside all the loops. (I have not shown this case in example).




回答29:


The way I solve this is by defining a variable that is referenced to determine if you break to the next level or not. In this example, this variable is called 'shouldbreak'.

Variable_That_Counts_To_Three=1
while 1==1:
    shouldbreak='no'
    Variable_That_Counts_To_Five=0
    while 2==2:
        Variable_That_Counts_To_Five+=1
        print(Variable_That_Counts_To_Five)
        if Variable_That_Counts_To_Five == 5:
            if Variable_That_Counts_To_Three == 3:
                shouldbreak='yes'
            break
    print('Three Counter = ' + str(Variable_That_Counts_To_Three))
    Variable_That_Counts_To_Three+=1
    if shouldbreak == 'yes':
        break

print('''
This breaks out of two loops!''')

This gives a lot of control over how exactly you want the program to break, allowing you to choose when you want to break and how many levels to go down.



来源:https://stackoverflow.com/questions/189645/how-to-break-out-of-multiple-loops

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!