Ответ 1
Моим первым инстинктом было бы рефакторинг вложенного цикла в функцию и использование return
для выхода.
Учитывая следующий код (это не работает):
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
Есть ли способ сделать эту работу? Или я должен сделать одну проверку, чтобы выйти из цикла ввода, а затем другую, более ограниченную, проверить во внешнем цикле, чтобы разорвать все вместе, если пользователь удовлетворен?
Моим первым инстинктом было бы рефакторинг вложенного цикла в функцию и использование return
для выхода.
Здесь другой подход, который является коротким. Недостатком является то, что вы можете разорвать только внешний цикл, но иногда это именно то, что вы хотите.
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
аккуратно обходить внешний разрыв.
PEP 3136 предлагает помеченные break/continue. Guido отклонил его, потому что "код настолько сложный, что требуется эта функция, очень редка". PEP упоминает некоторые обходные пути, хотя (например, метод исключения), в то время как Guido считает, что рефакторинг для использования возврата будет проще в большинстве случаев.
Во-первых, обычная логика полезна.
Если по какой-то причине условия завершения не могут быть выработаны, исключения - это откат.
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" нам нужен немедленный выход. Это более исключительное.
Я склонен согласиться с тем, что рефакторинг в функцию обычно является наилучшим подходом для такого рода ситуаций, но когда вам действительно нужно вырваться из вложенных циклов, вот интересный вариант подхода к сбору исключений, который @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
каждый раз.
Во-первых, вы также можете рассмотреть возможность создания и проверки ввода функции; внутри этой функции вы можете просто вернуть значение, если оно правильно, и продолжайте вращаться в цикле 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" и все такое, но он хорошо работает в таких странных случаях.
Ввести новую переменную, которую вы будете использовать как "выключатель цикла". Сначала присвойте ему что-то (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 # <--- !
Если у вас бесконечный цикл, это единственный выход; для других циклов выполнение действительно намного быстрее. Это также работает, если у вас много вложенных циклов. Вы можете выйти из всего, или только несколько. Безграничные возможности! Надеюсь, это помогло!
Чтобы разорвать несколько вложенных циклов без рефакторинга в функцию, используйте "симулированный оператор goto" со встроенным исключением StopIteration:
try:
for outer in range(100):
for inner in range(100):
if break_early():
raise StopIteration
except StopIteration: pass
См. Это обсуждение использования операторов goto для разрыва вложенных циклов.
keeplooping=True
while keeplooping:
#Do Stuff
while keeplooping:
#do some other stuff
if finisheddoingstuff(): keeplooping=False
или что-то в этом роде. Вы можете установить переменную во внутреннем цикле и проверить ее во внешнем цикле сразу же после выхода внутреннего цикла, если это необходимо. Я вроде как метод GOTO, если вы не против использовать модуль шуток April Fool - его не Pythonic, но он имеет смысл.
Это не самый красивый способ сделать это, но, на мой взгляд, это лучший способ.
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
Я уверен, что вы тоже можете что-то решить, используя рекурсию, но я не знаю, если это хороший вариант для вас.
И почему бы не продолжать цикл, если выполняются два условия? Я думаю, что это более 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
Не правда ли?
Все самое лучшее.
Обозначьте логику цикла в итераторе, который дает переменные цикла и возвращается, когда это делается, - вот простой пример, который выводит изображения в строках/столбцах, пока мы не выйдем из изображений или не поместим их:
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 ...
Преимущество состоит в том, чтобы разбить сложную логику цикла и обработку...
В этом случае, как отмечают другие, функциональная декомпозиция - это путь. Код в 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
В структуре 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
запускается после завершения цикла.
Моя причина приходить сюда в том, что у меня был внешний цикл и внутренний цикл:
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, но я размещаю его в надежде, что он поможет кому-то думать о своей проблеме по-другому, сохраняя при этом все просто.
# 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
Другим способом сокращения вашей итерации до одноуровневого цикла будет использование генераторов, также указанное в ссылка на 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` из всех циклов, но, к сожалению, он закрыт как дубликат этого
Используя функцию:
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
за раз снизу.
Простой способ превратить несколько циклов в один разрывный цикл - использовать 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 раз проще, чем большинство предлагаемых ответов.
Попробуйте использовать бесконечный генератор.
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
# 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
Вероятно, небольшой трюк, как показано ниже, сделает, если вы не хотите рефакторизовать функцию
добавлена 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
Вы можете определить переменную (например, 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
С примером: эти две матрицы равны/одинаковы?
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
Хорошего дня!
Напоминаю, что функции в 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
Надеюсь, это поможет:
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
//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;
}
Аналогично предыдущему, но более компактному. (Булевы - это просто числа)
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 # <--- !
Поскольку этот вопрос стал стандартным вопросом для взлома в конкретный цикл, я хотел бы дать свой ответ с помощью примера 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 вне всех циклов. (Я не показал этот случай в примере).
Я решаю это путем определения переменной, на которую ссылаются, чтобы определить, переходите ли вы на следующий уровень или нет. В этом примере эта переменная называется '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!''')
Это дает большой контроль над тем, как именно вы хотите, чтобы программа сломалась, позволяя вам выбирать, когда вы хотите сломать и сколько уровней пройти вниз.