Python/条件分岐と繰り返し
Python の制御構造には、逐次・分岐・繰返しの3つがあり。このうち逐次はプログラムが左上から左から右・上から下に進む当たり前の状態ですので、これ以上の説明は必要ないと思います[1]。
条件分岐[編集]
条件分岐とは、ある条件を満たすかどうかで、次に実行するプログラムの位置を変えることです。
if文[編集]
最も素朴な条件分岐は、単一のif文です[2]。
- if文による条件分岐
x = 3 if x == 2 : print("abcd")
- 条件分岐の上のコードの、
x == 2
とは、「xは2に等しい」という意味です。 - 冒頭の「x = 3」は、xに3を代入するという意味です。
- python では、if文にある条件式にある等号と、代入との等号を区別するために、条件分岐での等号には
x == 2
を使います。 - 上のコードでは、「xは2に等しい」の条件を満たさないため(xには3が代入されています)、実行しても文「abcd」は表示されません。
- また、pythonでは、if文の中の文(ブロックと言います)は、字下げをします(この様なインデントによる構造構文をオフサイドルールと言います)。
- インデントは半角スペース4つが推奨されます。
- 実行結果
※なにも表示されません。
つぎに、if文のなかのxの条件を変えてみて、条件が成り立つようにしましょう。
- 条件が真になるように変更
x = 3 if x == 3 : print("abcd")
- 上のコードでは、「xは3に等しい」の条件を満たすので、実行したら文「abcd」が表示されます。
- 実行結果
abcd
つまり、if文の構文は、次のようになります。
if 条件式 : 処理
- if文のブロックの外に文を追加
x = 3 if x == 2 : print("abcd") print("ef")
上のコードでは、print("ef")はif文のブロックの外にあることになります。 なぜなら、print("ef")は字下げされてないからです。 なので、プログラムを実行すると「ef」が表示されます。
いっぽう、次のコードでは、結果が変わります。
- if文のブロックに文を追加
x = 3 if x == 2 : print("abcd") print("ef")
上のコードでは、print("ef")はif文ブロックの中にあるので、プログラムを実行しても、「ef」は表示されませんし、もちろん「abcd」も表示されません。
ifブロック内は、字下げをそろえないと、エラーになります。
- インデントに誤りのある例
x = 3 if x == 2 : print("abcd") print("ef")
(↑エラーになります。)
下記コードでは、x==2
が満たされていないので、インデントされたブロックは実行されません。そのため実行すると「プログラムが終了しました。」だけが表示されます。
x = 3 if x == 2 : print("abcd") print("ef") print("プログラムが終了しました。")
条件式には、不等号(>
や<
など)も使えます。
x = 3 if x > 2 : print("xは2よりも大きい。") print("プログラムが終了しました。")
上のコードを実行すると「xは2よりも大きいよ。」と「プログラムが終了しました。」が表示されます。
比較演算子[編集]
記号>
のような、値を比較する演算子のことを比較演算子といいます。
pythonで使える比較演算子には、次の表のようなものがあります。
比較演算子 演算子 意味 >
左側の数が、右側の数よりも大きい <
左側の数が、右側の数よりも小さい ==
両辺の数値の大きさが等しい !=
等しくない >=
左側の数が、右側の数よりも大きいか、
または両辺が等しい<=
左側の数が、右側の数よりも小さいか、
または両辺が等しい
in 演算子[編集]
in 演算子は、オブジェクトがコレクションの要素にあるかを返します。
- in 演算子
li = [2,3,5] tu = (10,20,30) print(2 in li) print(2 in tu)
- 実行結果
True False
else節のあるif文[編集]
前の節では、if文の条件式によってブロックの実行/否を切り替えました。 ここでは、一歩進めて条件式に合致していなかった場合に別のブロックを実行するelse節を含んだ条件実行構文を紹介します[3]。
- else節のあるif文
x = 3 if x == 2 : print("xは2に等しい。") else : print("xは2に等しくありません。") print("プログラムが終了しました。")
- 実行結果
xは2に等しくないよ。 プログラムが終了しました。
なお、elseをつかわないでも、同じ内容の処理が書けます。
- else節を使わず、逆論理の別のif文を使った例
x = 3 if x == 2 : print("xは2に等しい。") if x != 2 : print("xは2に等しくありません。") print("プログラムが終了しました。")
- 2つの例は同じ動作をしますが、後者は「変更漏れの原因となる」という重大な欠点があるので、好んで逆論理のif文を使わずelseを使いましょ。
elif節のあるif文[編集]
else節を使うと、条件式によって2方向にプログラムフローを分けることができました。 実施のプログラムでは、2つ以上の条件式によってプログラムフローを分けることがあります。 そのような場合、次のように条件式ごとにif文が入れ子になります。
- else節のあるif文
x = float("nan") if x < 0 : print("xは0より小さい。") else : if x > 0 : print("xは0より大きい。") else : if x == 0 : print("xは0と等しい。") else : print("xは", x) print("プログラムが終了しました。")
- 実行結果
xはnan プログラムが終了しました。
このプログラムは意味的には正しいのですがインデントが深くなりすぎ、読むのも修正するのも難儀なので、Python にはelse節の中にif文が入れ子になっていことを表すキーワード elif が用意されています。
- elifによる置換え
x = float("nan") if x < 0 : print("xは0より小さい。") elif x > 0 : print("xは0より大きい。") elif x == 0 : print("xは0と等しい。") else : print("xは", x) print("プログラムが終了しました。")
- 実行結果
xはnan プログラムが終了しました。
- インデントが深くなりすぎず読みやすいですね。
if 式[編集]
Python には、if文の他、if式があります。
- コード例
x = 3 print("xは2に等しい。" if x == 2 else "xは2に等しくありません。") print("プログラムが終了しました。")
- 実行結果
xは2に等しくありません。 プログラムが終了しました。
- これで、else の例と同じ意味になります。
- PerlやRubyの後置のifと似ていますが else を伴うので、C言語系の条件演算子と(構文はまるで違いますが)機能的には同じです。
- 大概の場合、if文を使ったほうが読みやすいのですが、内包表記やラムダ式では式が要求されるので、if式を使う必要があります。
nanってナニ? |
唐突に float("nan") 、が出てきましたが、これがPythonでNaN(Not a Number; 非数)を得る方法で、他には math を import して math.nan を使う方法もあります。
nan は、浮動小数点演算の結果として、不正なオペランドを与えられたために生じた結果を表す値またはシンボルで[4]、NaNの体系的仕様は、無限大の表現などと共に1985年の IEEE 754 浮動小数点規格で標準が与えられています。
|
論理演算子[編集]
「変数xは2より大きくて、5より小さい」ときにだけ実行するような処理は、次のように書きます。
x = 3 if x > 2 and x < 5 : print("xは2より大きくて5より小さい。") else: print("そうでない場合。") print("プログラムが終了しました。")
and
は、両方の条件が成り立つという意味です。
上記のコードを実行すると、「
xは2より大きくて5より小さいよ。
」が表示されます。「そうでない場合。」は表示されません。
xを6に変えてみましょう。
x = 6 if x > 2 and x < 5 : print("xは2より大きくて5より小さい。") else: print("そうでない場合。") print("プログラムが終了しました。")
上記のコードを実行すると、「
そうでない場合。
」が表示されます。「xは2より大きくて5より小さいよ。」は表示されません。
演算子 and
をもちいて、
if 条件式A and 条件式B :
のように、使いうことにより、条件式Aと条件式Bが両方とも成り立つ場合に、「条件式A and 条件式B」が成り立つという意味にできます(数理論理学の用語でいう「論理積」です)。
演算子 | 意味 |
---|---|
and |
両方とも真。(「論理積」) |
or |
片方または両方が真。(「論理和」) |
not |
真ならば偽・偽ならば真。(「論理和」) |
複数の比較演算子の連結 |
上記の例では、and 演算子の説明のため
としていますが、これは
と書くこともできます。両者は同じ意味になりますがxの参照は後者は一度だけになります。 |
match文[編集]
Pythonには、C言語のswitch文はありませんでしたが、Python 3.10 から match 文がサポートされました[5]。
- match文を使った条件分岐
for x in range(10): print(x,"=", end=" ") match x: case 2: print('two') case 5: print('five') case 7: print('seven') case _: print('ELSE')
- 実行結果
0 = ELSE 1 = ELSE 2 = two 3 = ELSE 4 = ELSE 5 = five 6 = ELSE 7 = seven 8 = ELSE 9 = ELSE
- switch文のdefault節に相当する機能は、case節 の値を _ にすることによって実現します。
match文は、2021年に導入された新しい機能なので、3.10 より古い3.9以下バージョンのpythonではmatch文は使えません。なので古いバージョンでは、if文などで代用してください。
連想配列を使った多方向分岐[編集]
上の例は、連想配列を使って下のように書き換えることができます。
- 連想配列を使った例
h = { 2: "two", 5: "five", 7: "seven"} for x in range(10) : print(f"{x} =", x in h and h[x] or "ELSE")
列挙型[編集]
python では モジュール Enum で列挙型が提供されています[6]。 Enum はclassを用いる方法と、Enum関数を用いる方法の2つがありますが、ここでは class を用いる方法を紹介します。
- 列挙型
from enum import Enum class GameMode(Enum): map = 1 menu = 2 battle = 3 currentMode = GameMode.menu assert currentMode in GameMode if currentMode == GameMode.map: print('マップ画面を探検中') elif currentMode == GameMode.menu: print('キャンプを開いて体制を整えよう') elif currentMode == GameMode.battle: print('敵襲!戦闘開始だ!')
- 実行結果
キャンプを開いて体制を整えよう
map = 1
などのブロックの値は、べつに大小が順番どおりである必要はなく、それどころかWindowsで実験した限りは整数値である必要すらもなく小数値や数式ですら代入できてしまうのですが、しかしわざわざ分かりづらい数値を与える必要もないし、今後のバージョンの仕様変更なども想定して、なるべく普通の整数値を与えるのが安全だと思います。
なお、pythonの場合、enum変数をprint出力すると、下記のように格納された変数名(例の場合では"GameMode.menu")が出力されます。
enum変数自体には、「<GameMode.menu: 2>」のように変数名と値の両方のペアが格納されます。
それだけではenum変数を数値演算に活用できず不便なので、enum変数の数値を指定したい場合は下記のように.value
プロパティを指定します。
from enum import Enum
class GameMode(Enum):
map = 1
menu = 2
battle = 3
currentMode = GameMode.menu
assert currentMode in GameMode
if currentMode == GameMode.map:
print('マップ画面を探検中')
elif currentMode == GameMode.menu:
print('キャンプを開いて体制を整えよう')
elif currentMode == GameMode.battle:
print('敵襲!戦闘開始だ!')
print(currentMode)
print(repr(currentMode))
print(currentMode.value)
print(currentMode.name)
- 実行結果
キャンプを開いて体制を整えよう GameMode.menu <GameMode.menu: 2> 2 menu
.nameプロパティは上記のように、GameMode.menu ではなく menu の部分だけを指定します。
他のプログラミング言語では、enum変数のprint表示の形式は違うかもしれないので、それぞれの言語のマニュアルを参照してください。
Enum.auto()の使用[編集]
値をきめるのが面倒な場合、auto をインポートすれば、pythonが自動的にきめてくれます。
- Enum.auto()の使用
from enum import Enum, auto class GameMode(Enum): map = auto() menu = auto() battle = auto() currentMode = GameMode.map assert currentMode in GameMode if currentMode == GameMode.map: print('マップ画面を探検中') elif currentMode == GameMode.menu: print('キャンプを開いて体制を整えよう') elif currentMode == GameMode.battle: print('敵襲!戦闘開始だ!') print(currentMode.value)
- 実行結果
マップ画面を探検中 1
__str__を定義[編集]
メソッド __str__ を定義すると、str() をオーバーロードできます。
- __str__を定義
from enum import Enum, auto class GameMode(Enum): map = auto() menu = auto() battle = auto() def __str__(self) : labels = { self.map: "マップ画面を探検中", self.menu:"キャンプを開いて体制を整えよう", self.battle: "敵襲!戦闘開始だ!" } return labels[self] currentMode = GameMode.battle print(currentMode)
- 実行結果
敵襲!戦闘開始だ!
別解[編集]
列挙子の値は整数でなくてもよく、autoプロシージャを使うまでもなくメッセージ文字列そのものを値とし、値には self.value でアクセスしました。 併せて、クラスメソッドの実装例も追加しました。
- 別解
from enum import Enum class GameMode(Enum): map = "マップ画面を探検中" menu = "キャンプを開いて体制を整えよう" battle = "敵襲!戦闘開始だ!" def __str__(self) : return self.value @classmethod def names(cls): return [e.name for e in cls] @classmethod def values(cls): return [e.value for e in cls] @classmethod def hash(cls): return {e.name: e.value for e in cls} currentMode = GameMode.menu print(currentMode) print(GameMode.names()) print(GameMode.values()) print(GameMode.hash())
- 実行結果
キャンプを開いて体制を整えよう ['map', 'menu', 'battle'] ['マップ画面を探検中', 'キャンプを開いて体制を整えよう', '敵襲!戦闘開始だ!'] {'map': 'マップ画面を探検中', 'menu': 'キャンプを開いて体制を整えよう', 'battle': '敵襲!戦闘開始だ!'}
繰返し[編集]
繰返しに関するPythonの構文には、while文と for文の2つがあります。
while文[編集]
while 文は、ある条件を満たすあいです、処理を繰返します。
- 構文
while 条件式 : 処理
- コード例
i = "b" while i != "q": i = input("入力した文字を3個、ならべるよ。(終了したい場合はqを入力)") print(3*i)
- 条件式は自動的に論理型に変換されます。
- 上記のプログラムを実行すると、まず「入力した文字を3個、ならべるよ。(終了したい場合はqを入力)」と言われます。
- 入力文字として、たとえば「a」と入力すると、「aaa」と出力表示され、そして再び「入力した文字を3個、ならべるよ。(終了したい場合はqを入力)」と聞かれ、ふたたび入力を待つ状態になります。なので、「b」と入力してみると、「bbb」と出力表示されます。
- 上記のプログラムは、文字「q」が入力されないかぎり、動作がつづく。文字「q」を入力することにより、終了します。
代入演算子[編集]
上記の while 文の例は、代入演算子をつかうと、
- コード例(悪い例)
while (i := input("入力した文字を3個、ならべるよ。(終了したい場合はqを入力)")) != 'q' : print(3*i)
- のように書換えることができます。
while文とif文との組合わせ[編集]
while文はif文と組合わせることもできます。
- while文とif文との組合わせ(悪い例)
while (i := input("文字を入力しましょう。sを入力で物語がスタート。(終了したい場合はqを入力)")) != 'q' : if i == "s": print("はじまりはじまり。") print("むかし、むかし、あるところに、") print("おじいさんとおばあさんが、すんでいました。") print("おしまい")
- 上記のプログラムを実行すると、文字「s」を入力することで「はじまりはじまり。」と表示させることができ、そして「むかし、むかし、あるところに、」「おじいさんとおばあさんが、すんでいました。」「おしまい」と表示します。
- sを入力しなければ、「はじまりはじまり。」などは表示されません。
- このコードは、while の条件式に入力のロジックと終了条件のロジックを合成して読解と修正を困難にしている悪い例です。#break文に改善した例があります。
for文[編集]
- 素朴なfor文
for i in range(5): print("a")
- のように書いて実行すると、組込み関数range()のカッコ内の回数だけ、ブロック内の文を繰り返す。上のコードの場合、実行すると、「a」を5回、表示します。
- 実行結果
a a a a a
forのあとの変数(ループ変数と呼ばれます)は、別にiである必要はなく、特に以後値を参照しない場合は _ を使うと慣習的に「参照しないこと」が明示できます。
- ループ変数が以後参照しないことを明示している例
for _ in range(5): print("a")
ただし、あくまで慣習にすぎず、言語仕様上は _ という名前の変数にすぎないので、_ の値を参照するプログラムを書けば実行でき、インタプリターは何の警告もしません。 また、pylint の様な静的なソースコード診断ツールでは、_ は未使用変数のチェックの対象から外されているなど、言語仕様ではないものの _ を特別な識別子として扱う事は慣例的に定着しているので、_ の値を参照するコードを書かないことを強く推奨します。
慣習上、for文を書くときは、i で回数を数えることが多い[7]。
for i in range(5): print(i)
のように、すると、
- 実行結果
0 1 2 3 4
のように表示されます。 組込み関数 range() は range クラスのオブジェクトを返します。
range以外のオブジェクトとfor文の組み合わせ[編集]
- range以外のオブジェクトとfor文の組み合わせ
li = [2, 3, 5] tu = (10, 20, 30) for i in li: print(i, end=", ") else: print() for i in tu: print(i, end=", ") else: print() for i, j in zip(li, tu): print(i, j, sep=":", end=", ") else: print() print(type(zip(li, tu)))
- 実行結果
2, 3, 5, 10, 20, 30, 2:10, 3:20, 5:30, <class 'zip'>
continue文[編集]
continue文は、ループの先頭に戻ります。continue文を含むループのブロックのcontinue文以降は実行されません。
pass文[編集]
pass文は、なにもしません。 「なにもしないが構文上ブロックが要求される」ケースで使われます。
break文[編集]
while文あるいはfor文のブロックで文「break」を使うと、ループを中断します。
i = "b" while True: i = input("文字を入力しましょう。sを入力で物語がスタート。(終了したい場合はqを入力)) if i == "s": print("はじまりはじまり。") print("むかし、むかし、あるところに、") print("おじいさんとおばあさんが、すんでいました。") print("おしまい") elif i == "q": break
break文で中断する対象は、if文ではありません。break文は、ループを中断するものです。
なおwhile True
のように、while直後の条件式をブール値 True にすると、条件式は常に真となり、永久ループとなります[8]。
ループにつづくelse節[編集]
while文あるいはfor文がbreak文で中断しなかった場合、forにつづくelse節が実行されます。else節は、大概のプログラミング言語ではif文と組合わせて使いますが、Pythonではこれに加え繰返し(while文あるいはfor文)と組合わせることができます。
while+else[編集]
- while+else
i = 0 while i < 100: print(i, end=" ") i += 3 else: print("done!") i = 0 while i < 100: if i > 10: break print(i, end=" ") i += 3 else: print("done!")
- 実行結果
0 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99 done! 0 3 6 9
- ループを完走した場合は else 節を実行し、break (や return)で中断すると else 節は実行されません。
- 1つ目のwhileループは、変数
i
を0から3ずつ増やし、100以上になるまでループを繰り返します。ループの本体では、変数i
の値を印刷します。end=" "
は、印刷された各値の後に空白を追加し、すべてを1行に表示するためのものです。最後に、else節で"done!"というメッセージを印刷します。 - 2つ目のwhileループは、変数
i
を0から3ずつ増やし、10を超えるまでループを繰り返します。ループの本体では、変数i
の値を印刷し、それが10を超えた場合は、break
ステートメントが呼び出され、ループが終了します。最後に、else節が実行されず、"done!"というメッセージは印刷されません。
ループと結合したelseの使いどころ |
Pythonでは、ループが完走したときに実行されるelse節を置くことができます。
では、このelse節は何に使うのでしょう?
ループ完走のelseを使うと、「因数が見つからなかった」ケースをフラッグなしに表現でき、記述力が向上し、より洗練されたアルゴリズムの探求に貢献しています。 もし、素数集合更新版をフラッグで表現したら、これより遥かに記述性が劣るでしょう。 Python以外の言語では、Zigでも else を伴ったループ構文があります。 |
for+else[編集]
- for+else
h = {2: "two", 5: "five", 7: "seven"} for x in list(h): print(h[x]) else: print("I didn't take a break.") for x, v in h.items(): if x == 7: break print(x, v) else: print("I didn't take a break.")
- 実行結果
two five seven I didn't take a break. 2 two 5 five
- ループをカンストした場合は else 節を実行し、break (や return)で中断すると else 節は実行されません。
ループとelseを組合わせた大域脱出 |
Pythonはgoto分やラベル付きbreak文を持っていないので、大域脱出する場合は
などの工夫が必要です。 ここでは、ループと結合したelseを使った大域脱出を紹介します。
|
ループはスコープを作りません[編集]
Pythonでは、ループはスコープを作りません。
- ループはスコープを作りません
i = "abc" j = "xyz" print(f"forの前: {i=}、{j=}") for i in range(5): j = 2 * i print(f"forの中: {i=}、{j=}") print(f"forの後: {i=}、{j=}")
- 実行結果
forの前: i='abc'、j='xyz' forの中: i=0、j=0 forの中: i=1、j=2 forの中: i=2、j=4 forの中: i=3、j=6 forの中: i=4、j=8 forの後: i=4、j=8
脚註[編集]
- ^ 関数やメソッドの呼出しや復帰 return も制御構造と言えますが、本項では逐次・分岐・繰返しについて述べます。
- ^ “4.1. if Statements” (2021年11月17日). 2021年11月18日閲覧。
- ^ else節は、他の言語と違いfor文とも結合しますので注意してください。
- ^ ただし、Pythonでは math.asin(2) ↑ValueError であったりnanを返す前に例外が上がる事が多々あります。
- ^ “4.6. match Statements” (2021年11月17日). 2021年11月18日閲覧。
- ^ “enum — Support for enumerations” (2021年11月17日). 2021年11月18日閲覧。
- ^ 少なくともFortranではdo文のループ変数に i が常用され、それ以前に数学でも数列の和など i が使用されます。由来は、iteration の i; integer の i など諸説あります。
- ^
while 1:
としても無限ループになりますが、これは整数型が論理型に変換されるとき「0はFalse, 0以外はTrue」に暗黙に変換されることに頼っています(意味を明示するためwhile True:
とすべき)。同様にwhile i:
として、ループ変数 i がゼロになるまで繰り返すのようなコードも暗黙変換に頼っています。このようなコードは小さな変更で最初の意図に反した挙動を示します。たとえば i-=1 を i-=2 にした場合、i=1の次はi=-1となり、ループ終了条件のi=0を経ることなく無限ループになります。この場合はwhile i > 0:
とすべきです。