我怎样才能摆脱多重循环?

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

问题描述:

给出以下代码(不起作用):

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 没有。

最好的选择是:

  1. 设置一个由外循环检查的标志,或者设置外循环条件。

  2. 将循环放在函数中并使用 return 一次性跳出所有循环。

  3. 重新制定你的逻辑。

信用


使用函数

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触发块。既不会发生异常,continuebreak不会触发块。有关更多信息,请参阅“ 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 中的函数可以在代码中间创建,并且可以透明地访问周围的变量以进行读取,并使用nonlocalglobal声明进行写入。

因此,您可以将函数用作“可破坏的控制结构”,定义您想要返回的位置:

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

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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用