web-dev-qa-db-ja.com

Pythonで複数のループから抜け出す方法は?

次のようなコードを考えてみましょう(それはうまくいきません):

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回実行してから、さらに制限された別のチェックを行って、ユーザーが満足していれば、外側のループでチェックアウトするのですか。

391

私の最初の本能は入れ子になったループを関数にリファクタリングし、returnを使ってブレークアウトすることです。

420
Robert Rossney

これは別の短いアプローチです。不利な点は、外側のループだけを解除できることですが、ときにはそれがまさにあなたの望むことです。

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はきちんと外側のブレークを回避します。

162
yak

PEP 3136 はラベル付きbreak/continueを提案します。 Guido はそれを拒否しました 。「この機能を必要とするほど複雑なコードは非常にまれです」。ただし、PEPではいくつかの回避策(例外手法など)について言及していますが、Guidoでは、returnを使用するためのリファクタリングはほとんどの場合より簡単になると考えています。

141
John Fouhy

まず、通常のロジックが役に立ちます。

何らかの理由で終了条件を決定できない場合、例外はフォールバック計画です。

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"オプションの場合は、すぐに終了します。それはもっと例外的です。

101
S.Lott

私は、関数へのリファクタリングが通常この種の状況のた​​めの最善のアプローチであることに同意する傾向がありますが、あなたが本当にループから抜け出す必要があるときこれは、@ 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)nested_breakを使用するたびにカスタムビルドされたExceptionサブクラスが得られます。毎回あなた自身のExceptionサブクラスを宣言する必要はありません。

48
Mark Dickinson

まず、入力を取得して検証するプロセスを関数にすることも検討できます。その関数内では、正しい場合は値を返し、正しくない場合は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を使用してはいけません」そしてそれのすべて、しかしそれはこのような奇妙な場合にうまく機能します。

37
Matt J

「ループブレーカー」として使用する新しい変数を導入してください。最初に何かを代入し(False、0など)、次に外側のループの中から抜け出す前に、値を別のもの(True、1、...)に変更します。ループが終了したら、 'parent'ループでその値を確認します。説明しましょう。

breaker = False #our mighty loop exiter!
while True:
    while True:
        if conditionMet:
            #insert code here...
            breaker = True 
            break
    if breaker: # the interesting part!
        break   # <--- !

無限ループがある場合、これが唯一の解決策です。他のループのために実行は本当にずっと速いです。これはネストしたループがたくさんある場合にも機能します。あなたはすべてを終了することも、ほんの数個を終了することもできます。無限の可能性!これが役に立ったことを願っています!

26
krvolok

keeplooping=True
while keeplooping:
    #Do Stuff
    while keeplooping:
          #do some other stuff
          if finisheddoingstuff(): keeplooping=False

それとも何か。内側のループで変数を設定し、内側のループが終了した直後に外側のループでそれをチェックすることができます。エイプリルフールのジョークモジュールを使用しても構わないのであれば、GOTOメソッドが非常に気に入っています。これはPythonicではありませんが、意味があります。

14
quick_dry

関数にリファクタリングせずに複数の入れ子になったループから抜け出すには、組み込みの StopIteration例外 で "simulated goto statement"を使用します。

try:
    for outer in range(100):
        for inner in range(100):
            if break_early():
                raise StopIteration

except StopIteration: pass

入れ子になったループから抜け出すためのgotoステートメントの使用については この議論 を参照してください。

11
Justas

これはそれを行うための最も美しい方法ではありませんが、私の意見では、それが最善の方法です。

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
Jason Baker

そして、2つの条件が当てはまる場合、なぜループし続けないのですか?私はこれがもっと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

そうではありませんか?

ではごきげんよう。

8
Mauro Aspé

ループロジックを、ループ変数を生成して終了時に返すイテレータに分解します。これは、イメージがなくなるまで、または配置する場所がなくなるまで、イメージを行/列に配置する単純なものです。

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

これには、複雑なループロジックと処理を分割するという利点があります。

8

この場合、他の人によっても指摘されているように、機能的分解が進むべき道です。 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
3
Loax

Pythonのwhile ... else構造体には隠されたトリックがあります。これを使用すると、コードを大幅に変更したり追加したりすることなく、ダブルブレークをシミュレートできます。本質的にwhile条件が偽の場合、elseブロックがトリガされます。 continueまたはbreakのいずれの例外も、elseブロックをトリガーしません。詳細については、 " Pythonのwhile文の他の節 "、または Pythonのwhile(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ブロックはループの完了後にトリガされます。

3
holroy

反復を単一レベルのループに減らすもう1つの方法は、 Pythonリファレンス にも指定されているように、ジェネレータを使用することです。

for i, j in ((i, j) for i in A for j in B):
    print(i , j)
    if (some_condition):
        break

あなたはそれをループのためのいくつものレベルにスケールアップすることができます

欠点は、あなたがもはや単一のレベルだけを破ることができないということです。それはすべてか無かです。

もう1つの欠点は、whileループでは機能しないことです。私はもともとこの答えを Pythonに投稿したかった - すべてのループから抜け出した が、残念ながらこれはこれと重複して閉じた

2

私がここに来たのは、私は外側のループと内側のループを持っていたからです。

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に移動します。

これを解決するために私が見つけたのは、単に配列を2回実行することでした。

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の質問の特定のケースであることを知っています、しかし、私はそれが物事を単純にしながら誰かが彼らの問題について異なって考えるのを助けることを願ってそれを掲示しています。

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

関数を使って:

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を含む各行をコメントアウトして実行してみます。

2
Rafiq
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
RufusVS

複数のループを単一の壊れやすいループに変える簡単な方法は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倍単純に思えます。

1
one_observation

以下のような小さなトリックはおそらく、機能へのリファクタリングを好まない場合にはうまくいく

whileループ条件を制御するために1つのbreak_level変数を追加

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
Skycc

無限ジェネレータを使ってみてください。

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
1
robert king

変数を定義し(たとえばbreak_statement)、2つのブレーク条件が発生したときに別の値に変更して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
1
helmsdeep

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
0
user

2通りのソリューション

例を挙げれば、これら2つの行列は等しい/同じですか?
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

良い一日を!

0
Harun Altay

うまくいけば、これが役立ちます。

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
0
Daniel L.