问题:如何摆脱多重循环?
给出以下代码(不起作用):
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
有没有办法使这项工作?还是我要进行一次检查以打破输入循环,然后再进行另一项限制较大的检查(如果用户满意的话)在外部循环中一起分解?
回答 0
我的第一个直觉是将嵌套循环重构为一个函数,然后使用它return
来分解。
回答 1
这是另一种简短的方法。缺点是您只能打破外部循环,但是有时正是您想要的。
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在for和while循环之后为什么使用’else’?
关键见解:似乎外部循环总是在中断。但是,如果内部循环不中断,那么外部循环也不会。
这continue
句话是魔术。在for-else子句中。根据定义,如果没有内部中断,则会发生这种情况。在这种情况下,可以continue
巧妙地规避外部中断。
回答 2
PEP 3136建议标记为中断/继续。Guido 拒绝了它,因为“如此复杂的代码很少需要此功能”。PEP确实提到了一些解决方法(例如,异常技术),而Guido认为在大多数情况下,使用return进行重构将更为简单。
回答 3
首先,普通逻辑是有帮助的。
如果由于某种原因无法确定终止条件,则exceptions是后备计划。
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
对于此特定示例,可能不需要exceptions。
另一方面,在字符模式应用程序中,我们经常有“ Y”,“ N”和“ Q”选项。对于“ Q”选项,我们希望立即退出。那更exceptions。
回答 4
我倾向于同意重构为函数通常是解决这种情况的最佳方法,但是对于确实需要打破嵌套循环的情况,这是@ S.Lott描述的异常引发方法的一个有趣变体。它使用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
每次都声明自己的子类。
回答 5
首先,您还可以考虑使获取和验证输入的过程成为一个函数。在该函数中,您可以只返回正确的值,否则返回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”以及所有其他功能,但是在像这样的奇怪情况下它可以很好地工作。
回答 6
引入一个新变量,将其用作“循环断路器”。首先给它分配一些东西(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 # <--- !
如果您有无限循环,这是唯一的出路。对于其他循环,执行速度实际上要快得多。如果您有许多嵌套循环,这也适用。您可以全部退出,也可以退出几个。无限可能!希望这对您有所帮助!
回答 7
要打破多个嵌套循环而不重构为函数,请使用带有内置StopIteration异常的“模拟goto语句” :
try:
for outer in range(100):
for inner in range(100):
if break_early():
raise StopIteration
except StopIteration: pass
见这个讨论关于使用goto语句的嵌套循环的突破。
回答 8
keeplooping=True
while keeplooping:
#Do Stuff
while keeplooping:
#do some other stuff
if finisheddoingstuff(): keeplooping=False
或类似的东西。您可以在内部循环中设置一个变量,并在内部循环退出后立即在外部循环中检查它,如果合适的话,可以中断。我有点像GOTO方法,但前提是您不介意使用愚人节的笑话模块-它不是Python语言的,但确实有道理。
回答 9
这不是最漂亮的方法,但是我认为这是最好的方法。
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
我很确定您也可以在这里使用递归来解决问题,但是我不知道这是否对您来说是个好选择。
回答 10
如果两个条件都成立,为什么不继续循环呢?我认为这是一种更Python化的方式:
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
是不是
祝一切顺利。
回答 11
将循环逻辑分解为一个迭代器,该迭代器产生循环变量并在完成后返回-这是一个简单的示例,它以行/列的形式排列图像,直到我们没有图像或放置位置为止:
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 ...
这具有拆分复杂的循环逻辑和处理的优点。
回答 12
在这种情况下,正如其他人指出的那样,功能分解也是解决之道。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
回答 13
Python while ... else
结构中有一个隐藏的技巧,可用于模拟两次中断,而无需进行大量代码更改/添加。本质上,如果while
条件为假,else
则触发该块。既不exceptions,continue
也不break
触发该else
块。有关更多信息,请参见“ Python while语句上的Else子句 ”或while(v2.7)上的Python文档的答案。
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
在循环完成后将触发该块。
回答 14
将迭代减少到单级循环的另一种方法是通过使用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`,但不幸的是,它已作为此副本的副本关闭了
回答 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
尝试使用无限生成器。
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
回答 17
通过使用一个函数:
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
从底部开始注释掉包含一行的每一行来运行。
回答 18
将多个循环转换为单个可中断循环的一种简单方法是使用 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倍。
回答 19
# 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
回答 20
如果不喜欢将其重构为函数,可能会出现如下所示的小技巧
添加了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
回答 21
您可以定义一个变量(例如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
回答 22
我想提醒您,Python中的函数可以在代码中间创建,并且可以透明地访问周围的变量以进行读取以及使用with 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
回答 23
两种解决方案
举一个例子:这两个矩阵相等/相同吗?
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
祝你今天愉快!
回答 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
希望这会有所帮助:
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
回答 26
这是一个似乎可行的实现:
break_ = False
for i in range(10):
if break_:
break
for j in range(10):
if j == 3:
break_ = True
break
else:
print(i, j)
唯一的缺点是您必须break_
在循环之前定义。
回答 27
从语言层面上无法做到这一点。有些语言可以跳转,而其他语言则需要带参数,而python则不需要。
最好的选择是:
设置一个由外循环检查的标志,或设置外循环条件。
将循环放入函数中,然后使用return一次退出所有循环。
重新制定您的逻辑。
使用功能
def doMywork(data):
for i in data:
for e in i:
return
使用标志
is_break = False
for i in data:
if is_break:
break # outer loop break
for e in i:
is_break = True
break # inner loop break
回答 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
。
尽管在多重循环结构中没有名为“循环中断”的标签,但是我们可以利用用户定义的异常来进入我们选择的特定循环。考虑下面的示例,在该示例中,我们将在base-6编号系统中打印最多4位数字的所有数字:
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。(我没有在示例中显示这种情况)。