如何跳出多个循环?

723

给定以下代码(无法正常工作):

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

有没有办法让这个工作?还是我必须进行一次检查以跳出输入循环,然后在外部循环中进行另一个更有限的检查,以便在用户满意时一起退出?

193
为什么Python不能只有一个'break(n)',其中n是你想要跳出的层数? - Nathan
14
如果你嵌套了很多循环,使用C++的goto语句会非常方便。 - Drake Johnson
2
@Nathan 看看这个问题:为什么Python不原生支持像goto这样的关键字来跳出n个循环,Nathan给出了一个非常好的解释。 - Shivam Jha
3
Ned Batchelder有一个有趣的演讲,解决了“如何打破两个循环”的问题。剧透警告:将双重循环变为单一循环。 - Tiago Martins Peres
将循环放在try except块中,并通过引发异常来跳出循环。在我看来,这是最易读的方法。 - undefined
39个回答

2
这是一个看起来可行的实现方式:
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_

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

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

2

尝试使用无限生成器。

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

2

两种解决方案

举个例子:这两个矩阵是否相等?
matrix1和matrix2是大小相同的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

1
希望这可以帮助到你:
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

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

1

最近我遇到了这个问题,想要避免重复的返回语句,因为它可能会掩盖逻辑错误,所以看了一下@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

这种方法可能不适用于所有情况,但是它是可适应的,并且如果需要,它具有允许复制条件而不是潜在结果的优点。


1

我来这里的原因是因为我有一个外循环和一个内循环,就像这样:

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 问题的一个特定案例,但我发帖希望它能帮助有人在保持简单的同时以不同的方式思考他们的问题。


这可能不是Python。数组的类型是什么?可能是列表,但它包含什么?即使它包含整数,array.pop(x)也可能不会做你想要的事情。 - Veky
这是一个很好的观点。我找不到我引用的代码。对于任何阅读此内容的人,array.pop(i) "从数组中删除索引为i的项并返回它",根据Python文档。因此,需要获取数组中项目x的索引才能使此代码按预期工作。还有array.remove(x)函数可以执行所需的操作。我将修改上面的答案以修复该错误。这假定第二个数组不包含重复项,因为array.remove(x)仅会删除找到的第一个x实例。 - Nathan Garabedian
好的,我明白了。在这种情况下,只需使用 break 而不是 continue 就可以实现你想要的效果,对吧? :-) - Veky
是的,在这些示例中,为了效率和清晰度,您可能希望使用break而不是continue。 :) - Nathan Garabedian

1
你可以定义一个变量(例如 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

不错的观点,然而在我们内部感兴趣的每个级别上,我们都需要扫描那个变量。语言没有GoTo指令,在性能方面感觉真的很糟糕。 - Anatoly Alekseev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接