我怎样才能摆脱多重循环?
- 2024-11-20 08:44:00
- admin 原创
- 8
问题描述:
给出以下代码(不起作用):
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 range(10):
for b in range(20):
if something(a, b):
break # Break the inner loop...
else:
continue # Continue if the inner loop wasn't broken.
break # Inner loop was broken, break the outer.
这使用了 for / else 构造,解释如下:为什么 python 在 for 和 while 循环后使用“else”?
关键见解:外循环似乎总是中断。但如果内循环不中断,外循环也不会中断。
语句continue
是这里的魔法。它位于 for-else 子句中。根据定义,如果没有内部中断,就会发生这种情况。在这种情况下continue
巧妙地避开了外部中断。
解决方案 3:
PEP 3136建议使用 break/continue 标签。Guido拒绝了它,因为“如此复杂以至于需要此功能的代码非常少见”。不过,PEP 确实提到了一些解决方法(例如异常技术),而 Guido 认为在大多数情况下,重构以使用 return 会更简单。
解决方案 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:
引入一个新变量,用作“循环中断器”。首先为其分配一些值(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 # <--- !
如果您有一个无限循环,这是唯一的出路;对于其他循环,执行速度确实要快得多。如果您有许多嵌套循环,这也有效。您可以退出所有循环,也可以只退出几个循环。无限可能!希望这对您有所帮助!
解决方案 6:
我倾向于同意,对于这种情况,重构为函数通常是最好的方法,但是当您真正需要跳出嵌套循环时,这里有一个 @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 = input("Is this ok? (y/n)")
if ok == "y" or ok == "Y": raise mylabel
if ok == "n" or ok == "N": break
print("more processing")
Exception
优点:(1)它稍微干净一些(没有显式的 try-except 块),(2)每次使用时都会获得一个定制的子类nested_break
;无需Exception
每次都声明自己的子类。
解决方案 7:
首先,您还可以考虑将获取和验证输入的过程变成一个函数;在该函数中,如果输入正确,您可以直接返回该值,如果不正确,则继续在while循环中旋转。这基本上消除了您解决的问题,并且通常可以应用于更一般的情况(跳出多个循环)。如果您绝对必须在代码中保留此结构,并且真的不想处理簿记布尔值……
您还可以按以下方式使用goto (使用此处的愚人节模块):
#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”等等,但它在这种奇怪的情况下效果很好。
解决方案 8:
要跳出多重嵌套循环,无需重构为函数,可以使用带有内置StopIteration 异常的“模拟 goto 语句” :
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 方法,前提是您不介意使用愚人节笑话模块 - 它不是 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:
从语言层面上来说,没有办法做到这一点。有些语言有 goto,有些语言有 break,它带有一个参数,而 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
解决方案 13:
将循环逻辑分解为一个迭代器,该迭代器产生循环变量并在完成后返回——这是一个简单的迭代器,它将图像按行/列排列,直到我们没有图像或没有地方放置它们:
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 ...
这样做的好处是可以分离复杂的循环逻辑和处理......
解决方案 14:
将多个循环变成一个可中断循环的简单方法是使用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 倍。
解决方案 15:
Python 结构中有一个隐藏的技巧while ... else
,可用于模拟双重中断,而无需进行太多代码更改/添加。本质上,如果条件while
为假,则else
触发块。既不会发生异常,continue
也break
不会触发块。有关更多信息,请参阅“ Python while 语句中的 Else 子句”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
块在循环完成后触发。
解决方案 16:
在这种情况下,正如其他人指出的那样,功能分解是可行的方法。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
解决方案 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:
将迭代减少到单级循环的另一种方法是使用生成器,如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
out of all loops上发布这个答案,但不幸的是,它被关闭了,因为它是这个的重复
解决方案 19:
我想提醒你,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
解决方案 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 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
解决方案 22:
# 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
解决方案 23:
这是一个似乎可行的实现:
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_
在循环之前进行定义。
解决方案 24:
我个人的做法是使用一个布尔值,当我准备跳出外层循环时,它会进行切换。例如
while True:
#snip: print out current state
quit = False
while True:
ok = input("Is this ok? (y/n)")
if ok.lower() == "y":
quit = True
break # this should work now :-)
if ok.lower() == "n":
quit = True
break # This should work too :-)
if quit:
break
#do more processing with menus and stuff
解决方案 25:
两种解决方法
举个例子:这两个矩阵相等/相同吗?
矩阵1和矩阵2是相同大小,n,二维矩阵。
第一个解决方案,没有函数
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
解决方案 26:
如果不喜欢重构为函数,可能可以使用下面的小技巧
增加了 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
解决方案 27:
您可以定义一个变量(例如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
解决方案 28:
我来这里的原因是我有一个外循环和一个内循环,如下所示:
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 问题的一个具体例子,但我发布它是为了帮助人们以不同的方式思考他们的问题,同时保持事情简单化。
解决方案 29:
希望这有帮助:
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
解决方案 30:
我最近遇到了这个问题,为了避免重复的 return 语句(这可能会隐藏逻辑错误),我研究了 @yak 的想法。这在嵌套的 for 循环中效果很好,但不太优雅。另一种方法是在下一个循环之前检查条件:
b = None
for a in range(10):
if something(a, b): # should never = True if b is None
break
for b in range(20):
pass
这可能并不适用于所有地方,但它具有适应性,并且如果需要的话,它的优点是允许复制条件而不是潜在结果。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件