3. 実行制御#

3.1. for文(反復処理)#

Pythonでは\(i = 0, 1, \dots, 9\)の反復を、for文を使い、

for i in range(10):
    # 各反復における処理

と表現する。反復する処理はインデントを一つ下げて記述し、反復終了後の処理は元のインデントで記述する。インデントが反復処理の範囲を明示する手段となっていることに注意せよ。なお、C言語では、反復処理を次のように表現する(各反復における処理は{}で挟まれているブロックで表現され、インデントは任意である)。

for (int i = 0; i < 10; ++i) {
    // 各反復における処理
}

インデントの文字数

なお、多くのエディタでは、[Tab]キーでインデントを一つ下げ、[Back Space]キーでインデントを一つ上げることができる。インデントは空白文字4文字で表現されることが多いが、ソースコード中で一貫性が保たれていれば、インデントを表現する空白文字数は任意である(例えば2文字でもよい)。ただ、スタイルガイド(PEP 8)によると4文字のインデントが推奨されている。

以下のコードは文字wを10回出力する。print関数においてend=''を指定して、改行を抑制している。

for i in range(10):
    print('w', end='')
wwwwwwwwww

\(i=0, 1, \dots, 9\)に対して、\(1089 \times i\)を計算する。print関数に複数の式をカンマ区切りで与えると、その値がスペース区切りで出力される。

for i in range(10):
    print(i, 1089 * i)
0 0
1 1089
2 2178
3 3267
4 4356
5 5445
6 6534
7 7623
8 8712
9 9801

\(i = 1, 2, \dots, 9\)に対して反復を行うには、for i in range(1, 10):とする(\(i=1\)\(i=9\)\(i=2\)\(i=8\)のときの計算結果を見比べると面白い)。

for i in range(1, 10):
    print(i, 1089 * i)
1 1089
2 2178
3 3267
4 4356
5 5445
6 6534
7 7623
8 8712
9 9801

次に、\(1\)から\(10\)までの自然数の和、すなわち\(\sum_{i=1}^{10}i\)を求めたい。以下のコードは、変数sを定義せずにiの値を加算しようとしたため、「sが未定義である」というエラーが発生する。

for i in range(1, 11):
    s += i
s
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/tmp/ipykernel_206383/2530092115.py in <module>
      1 for i in range(1, 11):
----> 2     s += i
      3 s

NameError: name 's' is not defined

この問題に対処するには、反復を始める前にs = 0を実行し、変数sに初期値\(0\)を代入しておけばよい。

s = 0
for i in range(1, 11):
    s += i
s
55

反復の範囲を\(1\)から\(20\)までの奇数とするには、range(1, 21, 2)とする。

s = 0
for i in range(1, 21, 2):
    s += i
    print(i, s)
s
1 1
3 4
5 9
7 16
9 25
11 36
13 49
15 64
17 81
19 100
100

3.2. if文(条件分岐)#

条件に応じて異なる処理を実行するにはif文を使う。if文は、与えられた条件が満たされれば、インデントされた箇所のコードを実行する。

if 条件:
    # 条件が満たされた時に実行する処理

なお、C言語では、条件分岐を以下のように表現する(条件が満たされたときの処理は{}で挟まれているブロックで表現され、インデントは任意である)。

if (condition) {
    // 条件が満たされた時に実行する処理
}

以下のコードでは、37.5 <= xの評価結果は偽なので、何も出力されない(試しにx = 38.0などに変更して実行してみるとよい)。

x = 37.1
if 37.5 <= x:
    print('熱がある')

条件が満たされないときに実行する処理は、else節に記述する。

if 37.5 <= x:
    print('熱がある')
else:
    print('熱がない')    
熱がない

条件が満たされないとき、さらに別の条件を確認したいときは、elif節を使う。

if 37.5 <= x:
    print('熱がある')
elif 37.0 <= x:
    print('微熱がある')
else:
    print('熱がない')   
微熱がある

なお、if文において間違って代入文を用いるとエラーとなる(C言語ではエラーにならず、気づきにくいバグの原因となる)。

if x = 37.5:
    print('熱がギリギリある')
  File "/tmp/ipykernel_206383/1051765441.py", line 1
    if x = 37.5:
       ^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

3.2.1. 比較演算とブール値#

37.5 <= xのような比較演算は値を比較した結果をブール値 True または False で返す。

  • x == y: xyは等しい(代入文における=と誤用しないように注意せよ)

  • x != y: xyは等しくない

  • x < y: xyよりも小さい

  • x > y: xyよりも大きい

  • x <= y: xyよりも小さいか等しい

  • x >= y: xyよりも大きいか等しい

x
37.1
x == 37.5
False
x != 37.5
True
x < 37.5
True
x > 37.5
False
x <= 37.5
True
x >= 37.5
False

これまで見てきたif文による条件分岐は、以下の処理の合成である。

  1. if文に書かれている比較演算を評価する

  2. その評価結果がTrueならばif節を、Falseならばelse節を実行する

例えば、以下のコードでは37.5 <= xの比較演算を評価し、その結果がFalseであるのでelse節が実行される。

if 37.5 <= x:
    print('熱がある')
else:
    print('熱がない')
熱がない

3.2.2. 論理演算#

否定はnotで表現する(以下の例でnotを使うのは分かりにくいので、それぞれ、x < 37.5およびx != 37.5と書くべきである)。

not x >= 37.5
True
not x == 37.5
True

真理値TrueFalseは組み込み定数として用意されている。

True
True
False
False
not True
False
not False
True

複数の条件の論理積を取るには、andを用いる。

if 37.0 <= x and x < 37.5:
    print('微熱がある')
微熱がある

なお、上のコードは次のように比較演算子の連結として表現できる。

if 37.0 <= x < 37.5:
    print('微熱がある')
微熱がある

複数の条件の論理和を取るには、orを用いる。

n = 3
if n == 1 or n == 6 or n == 8 or n == 10:
    s = 'ぽん'
elif n == 3:
    s = 'ぼん'
else:
    s = 'ほん'
s
'ぼん'

なお、上のコードは次のようにin演算子とタプル(..., ...)で表現することができる(タプルの詳細についてはここでは説明しない)。n in (1, 6, 8, 10)nが集合\(\{1, 6, 8, 10\}\)に含まれる場合は真、含まれない場合は偽となる。

if n in (1, 6, 8, 10):
    s = 'ぽん'
elif n == 3:
    s = 'ぼん'
else:
    s = 'ほん'
s
'ぼん'

in演算子の否定として、not in演算子が用意されている。

n = 4
n in (2, 4, 6, 8, 10)
True
n not in (2, 4, 6, 8, 10)
False

上の条件は以下のように書くこともできるが、上の書き方の方が分かりやすい(英語としてそのまま読める)。

not n in (2, 4, 6, 8, 10)
False

比較演算子や論理演算子の優先順位は、in, not in, ==, !=, <, >, <=, >= > not > and > orである。優先順位は括弧で変更できる。

not n == 2 or n == 4 or n == 6 or n == 8 or n == 10
True
not (n == 2 or n == 4 or n == 6 or n == 8 or n == 10)
False

3.2.3. 条件式#

代入とif文を組み合わせたような書き方がができる。C言語の三項演算子(条件 ? 真の場合 : 偽の場合)に対応する。

n = 5
s = '偶数' if n % 2 == 0 else '奇数'
s
'奇数'

複数の条件式をネストさせて書くこともできる。以下の例では、n in (1, 6, 8, 10)の条件の評価がn == 3よりも先に行われる。

s = 'ぽん' if n in (1, 6, 8, 10) else 'ぼん' if n == 3 else 'ほん'
s
'ほん'

3.3. while文(条件付き反復)#

while文は、与えられた条件が満たされている間、インデントが一つ下がった箇所のコードを繰り返し実行する。

while 条件:
    # 条件が満たされている間に反復する処理

なお、C言語での書き方も同様である(条件が満たされている間の処理は{}で挟まれているブロックで表現され、インデントは任意である)。

while (condition) {
    // 条件が満たされた時に実行する処理
}

変数\(n\)に任意の正の整数を代入し、\(n\)が偶数ならば2で割り、\(n\)が奇数ならば\(n \leftarrow 3n+1\)とする処理を、\(n=1\)になるまで繰り返す(コラッツの問題)。

n = 36
while n != 1:
    if n % 2 == 0:
        n //= 2
    else:
        n = 3 * n + 1
    print(n, end=' ')
18 9 28 14 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 

while True:で無限ループとなる。ループから抜けるにはbreak文を使う。

以下のプログラムは、\(f(x) = x^2 - 2 = 0\)の解の一つである\(\sqrt{2}\)Newton-Raphson法で求める(fxgxはそれぞれ、\(f(x)\)\(f'(x)\)の値を格納する)。より正確には、Newton-Raphson法の更新式、

\[ x \leftarrow x - \frac{f(x)}{f'(x)} \]

を繰り返し適用し、\(|f(x)| < 10^{-8}\)が満たされたときに無限ループから抜ける。

x = 1
while True:
    fx = x ** 2 - 2
    gx = 2 * x
    print(x, fx)
    if -1e-8 < fx < 1e-8:
        break
    x -= fx / gx
1 -1
1.5 0.25
1.4166666666666667 0.006944444444444642
1.4142156862745099 6.007304882871267e-06
1.4142135623746899 4.510614104447086e-12

無限ループの強制終了

無限ループはプログラムが停止しなくなる恐れがある。もしプログラムが停止しなくなった場合は、Pythonの対話型シェルであればキーボードで[Ctrl]+[c]を押し、Jupyter Notebookであればメニューから"Kernel" - "Interrupt Kernel"を選択し、プログラムの実行を強制的に停止できる。

3.4. continue文#

for文やwhile文による反復中にcontinue文を実行すると、以降の処理を実行せずに次の反復に移る(C言語と同じ)。

N = 10
for n in range(2, N+1):
    for a in range(2, n//2+1):
        if n % a != 0:
            continue
        print(n, '=', a, '*', n//a)
        break
4 = 2 * 2
6 = 2 * 3
8 = 2 * 4
9 = 3 * 3
10 = 2 * 5

3.5. break文#

for文やwhile文による反復中にbreak文を実行すると、以降の処理を実行せずに反復を終了する(C言語と同じ)。

N = 10
for n in range(2, N+1):
    for a in range(2, n//2+1):
        if n % a == 0:
            print(n, '=', a, '*', n//a)
            break
4 = 2 * 2
6 = 2 * 3
8 = 2 * 4
9 = 3 * 3
10 = 2 * 5

3.6. for文およびwhile文のelse節#

for文による反復において、反復が最後まで到達した後に実行する処理をelse節に記述できる。while文による反復では、条件が満たされなくなったときの処理をelse節に記述できる。

N = 10
for n in range(2, N+1):
    for a in range(2, n//2+1):
        if n % a == 0:
            print(n, '=', a, '*', n//a)
            break
    else:
        print(n, 'は素数')
2 は素数
3 は素数
4 = 2 * 2
5 は素数
6 = 2 * 3
7 は素数
8 = 2 * 4
9 = 3 * 3
10 = 2 * 5

3.7. match文 (Python 3.10以降)#

変数の値によって条件分岐を行いたいとき、条件が多くなるとelif節で何度も同じ変数を書く必要が生じる。C言語ではswitch文を使ってシンプルに書くことができたが、Pythonには類似した構文が用意されていなかった。

n = 3
if n == 1:
    print('one')
elif n == 2:
    print('two')
elif n == 3:
    print('three')
else:
    print('many')
three

Python 3.10でmatch文が導入され、上記の処理は次のように書けるようになった。case文で照合したい値を指定していくと、上から順番にmatch文で指定した式と照合が行われていく。照合できなかった場合の処理は、case _で記述できる。

n = 3
match n:
    case 1:
        print('one')
    case 2:
        print('two')
    case 3:
        print('three')
    case _:
        print('many')
three

複数の値のいずれかで照合をしたい場合は、演算子|を使う。

match n:
    case 1 | 6 | 8 | 10:
        s = 'ぽん'
    case 3:
        s = 'ぼん'
    case _:
        s = 'ほん'
s
'ぼん'

C言語において複数の値のいずれかで照合させるには、case文を並べて書いていたが、この書き方は文法エラーとなる。

match n:
    case 1:
    case 6:
    case 8:
    case 10:
        s = 'ぽん'
    case 3:
        s = 'ぼん'
    case _:
        s = 'ほん'
s
  File "/tmp/ipykernel_206383/3715514536.py", line 3
    case 6:
    ^
IndentationError: expected an indented block after 'case' statement on line 2