Как вырваться из нескольких петель?

Учитывая следующий код (это не работает):

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

Есть ли способ сделать эту работу? Или я должен сделать одну проверку, чтобы выйти из цикла ввода, а затем другую, более ограниченную, проверить во внешнем цикле, чтобы разорвать все вместе, если пользователь удовлетворен?

Ответ 1

Моим первым инстинктом было бы рефакторинг вложенного цикла в функцию и использование return для выхода.

Ответ 2

Здесь другой подход, который является коротким. Недостатком является то, что вы можете разорвать только внешний цикл, но иногда это именно то, что вы хотите.

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

Это использует конструкцию for/else, объясненную в: Почему Python использует 'else' после циклов for и while?

Основная идея: только кажется, что внешний цикл всегда прерывается. Но если внутренний цикл не прерывается, внешний цикл тоже не будет.

Утверждение continue - волшебство здесь. Это в пункте for-else. По определению это происходит, если нет внутреннего разрыва. В этой ситуации continue аккуратно обходить внешний разрыв.

Ответ 3

PEP 3136 предлагает помеченные break/continue. Guido отклонил его, потому что "код настолько сложный, что требуется эта функция, очень редка". PEP упоминает некоторые обходные пути, хотя (например, метод исключения), в то время как Guido считает, что рефакторинг для использования возврата будет проще в большинстве случаев.

Ответ 4

Во-первых, обычная логика полезна.

Если по какой-то причине условия завершения не могут быть выработаны, исключения - это откат.

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

В этом конкретном примере исключение может не понадобиться.

С другой стороны, мы часто используем опции "Y", "N" и "Q" в приложениях с символьным режимом. Для опции "Q" нам нужен немедленный выход. Это более исключительное.

Ответ 5

Я склонен согласиться с тем, что рефакторинг в функцию обычно является наилучшим подходом для такого рода ситуаций, но когда вам действительно нужно вырваться из вложенных циклов, вот интересный вариант подхода к сбору исключений, который @S. Лотт описал. Он использует оператор Python with, чтобы сделать сборку исключений более приятным. Определите новый менеджер контекста (вам нужно сделать это только один раз):

from contextlib import contextmanager
@contextmanager
def nested_break():
    class NestedBreakException(Exception):
        pass
    try:
        yield NestedBreakException
    except NestedBreakException:
        pass

Теперь вы можете использовать этот менеджер контекста следующим образом:

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"

Преимущества: (1) он немного чище (без явного блока try-except), и (2) вы получаете собственный подкласс Exception для каждого использования nested_break; нет необходимости объявлять свой собственный подкласс Exception каждый раз.

Ответ 6

Во-первых, вы также можете рассмотреть возможность создания и проверки ввода функции; внутри этой функции вы можете просто вернуть значение, если оно правильно, и продолжайте вращаться в цикле while, если нет. Это существенно устраняет проблему, которую вы разрешили, и обычно ее можно применять в более общем случае (разрыв нескольких циклов). Если вы абсолютно должны сохранить эту структуру в своем коде и действительно не хотите иметь дело с буферизацией буферов...

Вы также можете использовать goto следующим образом (используя модуль April Fools из здесь):

#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

Я знаю, я знаю, "ты не должен использовать goto" и все такое, но он хорошо работает в таких странных случаях.

Ответ 7

Ввести новую переменную, которую вы будете использовать как "выключатель цикла". Сначала присвойте ему что-то (False, 0 и т.д.), А затем внутри внешнего цикла, прежде чем вы перейдете от него, измените значение на что-то еще (True, 1,...). Как только петля выйдет, проверка "родительского" цикла для этого значения. Позвольте мне продемонстрировать:

breaker = False #our mighty loop exiter!
while True:
    while True:
        if conditionMet:
            #insert code here...
            breaker = True 
            break
    if breaker: # the interesting part!
        break   # <--- !

Если у вас бесконечный цикл, это единственный выход; для других циклов выполнение действительно намного быстрее. Это также работает, если у вас много вложенных циклов. Вы можете выйти из всего, или только несколько. Безграничные возможности! Надеюсь, это помогло!

Ответ 8

Чтобы разорвать несколько вложенных циклов без рефакторинга в функцию, используйте "симулированный оператор goto" со встроенным исключением StopIteration:

try:
    for outer in range(100):
        for inner in range(100):
            if break_early():
                raise StopIteration

except StopIteration: pass

См. Это обсуждение использования операторов goto для разрыва вложенных циклов.

Ответ 9


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

или что-то в этом роде. Вы можете установить переменную во внутреннем цикле и проверить ее во внешнем цикле сразу же после выхода внутреннего цикла, если это необходимо. Я вроде как метод GOTO, если вы не против использовать модуль шуток April Fool - его не Pythonic, но он имеет смысл.

Ответ 10

Это не самый красивый способ сделать это, но, на мой взгляд, это лучший способ.

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

Я уверен, что вы тоже можете что-то решить, используя рекурсию, но я не знаю, если это хороший вариант для вас.

Ответ 11

И почему бы не продолжать цикл, если выполняются два условия? Я думаю, что это более pythonic путь:

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

Не правда ли?

Все самое лучшее.

Ответ 12

Обозначьте логику цикла в итераторе, который дает переменные цикла и возвращается, когда это делается, - вот простой пример, который выводит изображения в строках/столбцах, пока мы не выйдем из изображений или не поместим их:

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

Преимущество состоит в том, чтобы разбить сложную логику цикла и обработку...

Ответ 13

В этом случае, как отмечают другие, функциональная декомпозиция - это путь. Код в 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

В структуре Python while ... else есть скрытый трюк, который можно использовать для имитации двойного разрыва без значительных изменений/дополнений кода. По сути, если условие while является ложным, срабатывает блок else. Ни исключения, continue, ни break не запускают блок else. Для получения дополнительной информации см. Ответы на предложение Else в инструкции Python while "или документ Python во время (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

Единственным недостатком является то, что вам нужно переместить условие двойного нарушения в условие while (или добавить переменную флага). Вариации этого существуют также для цикла for, где блок else запускается после завершения цикла.

Ответ 15

Моя причина приходить сюда в том, что у меня был внешний цикл и внутренний цикл:

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

Как вы можете видеть, он фактически не перейдет к следующему x, но вместо этого перейдет к следующему y.

то, что я нашел для этого, просто состояло в том, чтобы просто запустить массив:

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

Я знаю, что это был конкретный случай вопроса OP, но я размещаю его в надежде, что он поможет кому-то думать о своей проблеме по-другому, сохраняя при этом все просто.

Ответ 16

# 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

Ответ 17

Другим способом сокращения вашей итерации до одноуровневого цикла будет использование генераторов, также указанное в ссылка на python

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

Вы можете масштабировать его до любого количества уровней для цикла

Недостатком является то, что вы больше не можете ломать только один уровень. Это все или ничего.

Другим недостатком является то, что он не работает с циклом while. Я изначально хотел опубликовать этот ответ на Python -` break` из всех циклов, но, к сожалению, он закрыт как дубликат этого

Ответ 18

Используя функцию:

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()

Попробуйте запустить приведенные выше коды, также закомментировав return.

Без использования какой-либо функции:

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

Теперь запустите приведенные выше коды как есть, а затем попробуйте запустить, закомментировав каждую строку, содержащую по одному break за раз снизу.

Ответ 19

Простой способ превратить несколько циклов в один разрывный цикл - использовать 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!

Вы должны индексировать свои объекты, в отличие от возможности явно перебирать значения, но, по крайней мере, в простых случаях это кажется примерно в 2-20 раз проще, чем большинство предлагаемых ответов.

Ответ 20

Попробуйте использовать бесконечный генератор.

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

Ответ 21

# 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

Ответ 22

Вероятно, небольшой трюк, как показано ниже, сделает, если вы не хотите рефакторизовать функцию

добавлена ​​1 переменная break_level для управления состоянием цикла while

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

Ответ 23

Вы можете определить переменную (например, break_statement), а затем изменить ее на другое значение при возникновении условия двух разрывов и использовать его в if, чтобы разбить и на второй цикл.

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

Ответ 24

Решения в 2 пути

С примером: эти две матрицы равны/одинаковы?
matrix1 и matrix2 имеют одинаковый размер, n, 2 размерных матрицы.

Первое решение, без функции

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

Второе решение, с функцией
Это окончательное решение для моего случая

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

Хорошего дня!

Ответ 25

Напоминаю, что функции в Python можно создавать прямо в середине кода и прозрачно обращаться к окружающим переменным для чтения и с объявлением nonlocal или global для записи.

Итак, вы можете использовать функцию как "разрушаемую структуру управления", определяя место, в которое хотите вернуться:

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

Надеюсь, это поможет:

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

//it a C/C++ code in which it finds set of 3 numbers whose sum is 1000.
//getting out of multiple loops

//Methord 1
#include <stdio.h>
int main(){
int n=500;
for(int i=0;i<n;i++){
    for(int j=0;j<n;j++){
        for(int k=0;k<n;k++){
            if(i+j+k==1000){
                printf("%d %d %d ",i,j,k);
                goto exit;
            }
        }
    }
}
exit:
return 0;
}

//Methord 2
//The methord 1 was langauge specific ,but not all langauage have goto statement.
//hence we have alternative methord.

#include <stdio.h>
#include <math.h>
//getting out of multiple loops
int main(){
int n=500,exit=0;
for(int i=1;i<n;i++){
    for(int j=1;j<n;j++){
        for(int k=1;k<n;k++){
            if(i*i + j*j + k*k == 1000){
                printf("%d %d %d ",i,j,k);
                exit=1;
                break;
            }
        }
        if(exit==1)
            break;
    }
    if(exit==1)
        break;
}
return 0;
}

Ответ 28

Аналогично предыдущему, но более компактному. (Булевы - это просто числа)

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   # <--- !

Ответ 29

Поскольку этот вопрос стал стандартным вопросом для взлома в конкретный цикл, я хотел бы дать свой ответ с помощью примера Exception.

Несмотря на отсутствие метки с именем break of loop в конструкции с множественным циклом, мы можем использовать Пользовательские исключения, чтобы разбить определенную петлю нашего выбора. Рассмотрим следующий пример, где мы будем печатать все числа до 4 цифр в системе нумерации base-6:

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

Когда мы печатаем вывод, мы никогда не получим никакого значения, чье единичное место с 4. В этом случае мы не прерываем ни один цикл, когда BreakLoop(4) поднимается и попадает в тот же цикл. Аналогично, всякий раз, когда десять мест имеют 3, мы вступаем в третий цикл, используя BreakLoop(3). Всякий раз, когда сто места имеют 5, мы ломаемся во второй цикл, используя BreakLoop(2), и когда тысяча мест имеет 2, мы разбиваемся на первый цикл, используя BreakLoop(1).

Короче говоря, поднимите Exception (встроенный или определенный пользователем) во внутренних циклах и поймайте его в цикле, откуда вы хотите возобновить управление. Если вы хотите выйти из всех циклов, поймите Exception вне всех циклов. (Я не показал этот случай в примере).

Ответ 30

Я решаю это путем определения переменной, на которую ссылаются, чтобы определить, переходите ли вы на следующий уровень или нет. В этом примере эта переменная называется 'mustbreak'.

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!''')

Это дает большой контроль над тем, как именно вы хотите, чтобы программа сломалась, позволяя вам выбирать, когда вы хотите сломать и сколько уровней пройти вниз.