次のようなコードを考えてみましょう(それはうまくいきません):
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
を使ってブレークアウトすることです。
これは別の短いアプローチです。不利な点は、外側のループだけを解除できることですが、ときにはそれがまさにあなたの望むことです。
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
はきちんと外側のブレークを回避します。
PEP 3136 はラベル付きbreak/continueを提案します。 Guido はそれを拒否しました 。「この機能を必要とするほど複雑なコードは非常にまれです」。ただし、PEPではいくつかの回避策(例外手法など)について言及していますが、Guidoでは、returnを使用するためのリファクタリングはほとんどの場合より簡単になると考えています。
まず、通常のロジックが役に立ちます。
何らかの理由で終了条件を決定できない場合、例外はフォールバック計画です。
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"オプションの場合は、すぐに終了します。それはもっと例外的です。
私は、関数へのリファクタリングが通常この種の状況のための最善のアプローチであることに同意する傾向がありますが、あなたが本当にループから抜け出す必要があるときこれは、@ 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
サブクラスを宣言する必要はありません。
まず、入力を取得して検証するプロセスを関数にすることも検討できます。その関数内では、正しい場合は値を返し、正しくない場合は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を使用してはいけません」そしてそれのすべて、しかしそれはこのような奇妙な場合にうまく機能します。
「ループブレーカー」として使用する新しい変数を導入してください。最初に何かを代入し(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 # <--- !
無限ループがある場合、これが唯一の解決策です。他のループのために実行は本当にずっと速いです。これはネストしたループがたくさんある場合にも機能します。あなたはすべてを終了することも、ほんの数個を終了することもできます。無限の可能性!これが役に立ったことを願っています!
keeplooping=True
while keeplooping:
#Do Stuff
while keeplooping:
#do some other stuff
if finisheddoingstuff(): keeplooping=False
それとも何か。内側のループで変数を設定し、内側のループが終了した直後に外側のループでそれをチェックすることができます。エイプリルフールのジョークモジュールを使用しても構わないのであれば、GOTOメソッドが非常に気に入っています。これはPythonicではありませんが、意味があります。
関数にリファクタリングせずに複数の入れ子になったループから抜け出すには、組み込みの StopIteration例外 で "simulated goto statement"を使用します。
try:
for outer in range(100):
for inner in range(100):
if break_early():
raise StopIteration
except StopIteration: pass
入れ子になったループから抜け出すためのgotoステートメントの使用については この議論 を参照してください。
これはそれを行うための最も美しい方法ではありませんが、私の意見では、それが最善の方法です。
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
私はあなたがここでも再帰を使って何かを考え出すことができると確信しています、しかしそれがあなたにとって良い選択であるなら私はしません。
そして、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
そうではありませんか?
ではごきげんよう。
ループロジックを、ループ変数を生成して終了時に返すイテレータに分解します。これは、イメージがなくなるまで、または配置する場所がなくなるまで、イメージを行/列に配置する単純なものです。
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 ...
これには、複雑なループロジックと処理を分割するという利点があります。
この場合、他の人によっても指摘されているように、機能的分解が進むべき道です。 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
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
ブロックはループの完了後にトリガされます。
反復を単一レベルのループに減らすもう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に投稿したかった - すべてのループから抜け出した が、残念ながらこれはこれと重複して閉じた
私がここに来たのは、私は外側のループと内側のループを持っていたからです。
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の質問の特定のケースであることを知っています、しかし、私はそれが物事を単純にしながら誰かが彼らの問題について異なって考えるのを助けることを願ってそれを掲示しています。
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
関数を使って:
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
を含む各行をコメントアウトして実行してみます。
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
複数のループを単一の壊れやすいループに変える簡単な方法は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倍単純に思えます。
以下のような小さなトリックはおそらく、機能へのリファクタリングを好まない場合にはうまくいく
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
無限ジェネレータを使ってみてください。
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
変数を定義し(たとえば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
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
例を挙げれば、これら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
良い一日を!
うまくいけば、これが役立ちます。
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