[python] ネストループを一気にbreak, continueする

 for文などのループを途中で抜けるためにはbreakやcontinueを使いますが、外側からも一気に抜けたいことがあります。

for〜elseを使う

 Python3のdocによると、

最初のスイートの中で break 文が実行されると、 else 節のスイートを実行することなくループを終了します。 continue 文が最初のスイート内で実行されると、スイート内にある残りの文の実行をスキップして、次の要素の処理に移るか、これ以上次の要素が無い場合は else 節の処理に移ります。

 だそうです。つまり、for文内部でbreakが実行されるとelse文がスキップされます。なんだか直感的じゃないような気もしますが、これを活用すれば以下のように書けそうです。

for i in range(10):
  for j in range(10):
    print(f"{i} : {j}")
    if i == 5 and j == 5:
      break
  else:
    continue
  break
print("end")

 これで”5 : 5″までコンソールに出力された時点で外側のループからも抜けます。何もしなければelse節に移るので、そこでcontinueして直下のbreak文を毎回飛ばすことで、ループさせています。

 直感的であろうはずがありませんし、知ってる人が見たとしてもhackyであり、決して可読性に優れているとは思えません。

メソッドに切り出す

 なので、メソッドに切り出すのが良い方法です。こちらのほうが余程シンプルに書けます。

def loop():
  for i in range(10):
    for j in range(10):
      print(f"{i} : {j}")
      if i == 5 and j == 5:
        return

loop()
print("end")

 returnでいつでも抜けられますから、可読性にも優れていますね。以下に3重ネストの例を書いておきましょう。内側のループのみをreturnで抜けますが、外側はそのまま続きます。

def innerloop(outer_i):
  for i in range(10):
    for j in range(10):
      print(f"{outer_i} : {i} : {j}")
      if i == 5 and j == 5:
        return

for i in range(3):
  innerloop(i)

print("end")

 ”0 : 5 : 5″、 ”1 : 5 : 5″、 “2 : 5 : 5″で内側のループが終わっていることがわかります。

 また、外側のループを内側の条件分岐で抜けたい場合もあります。この場合は、以下のようにreturn文に意味を持たせれば上手く書けるはずです。

def innerloop(outer_i):
  for i in range(10):
    for j in range(10):
      print(f"{outer_i} : {i} : {j}")
      if i == 5 and j == 5:
        return outer_i == 1  # 外側のループのiが1ならTrueを返す

for i in range(3):
  should_break = innerloop(i)
  if should_break:
    break  # innerloopの返り値がTrueならbreakする

print("end")

まとめ

 for〜elseは構文としてはあるものの、外側のループを制御するために使うのは可読性を損ないますし、あまり筋が良い方法ではありません。適切にメソッドを切り分けることで、簡潔な書き方をすることができます。どんどんメソッドを活用していきましょう。