3. 条件によって動作を変える

今回は、条件によってプログラムの動作を変える方法を学んでいきます。

3.1. 命題と論理式

まずは、高校数学の復習です。

命題

正しいか正しくないかを判定できる文や式

  • \(3\)\(2\)より大きい

  • \(3\)は偶数である

命題が正しいときを真(true)、間違っているときは偽(false)といいます。

だから、「\(3\)\(2\)より大きい」は真ですが、「\(3\)は偶数である」は偽となります。

Python では、論理式として命題を表現することができます。

論理式:Python における命題

評価すると論理値(True もしくは False)になる式

このような論理式を表現するため、次のような演算子を提供しています。

数式

Python

説明

\(x < y~~~~~\)

x < y

x は y より小さいかどうか

\(x \le y\)

x <= y

x は y 以下かどうか

\(x > y\)

x > y

x は y より大きいかどうか

\(x \ge y\)

x >= y

x は y 以上かどうか

\(x = y\)

x == y

x は y と等しいかどうか

\(x \ne y\)

x != y

x は y と等しくないかどうか

\(x \in y\)

x in y

x は y に含まれるかどうか

\(x \not\in y\)

x not in y

x は y に含まれないかどうか

\(x \land y\)

x and y

x かつ y

\(x \lor y\)

x or y

x または y

\(\lnot x\)

not x

x の否定

例題(論理式)

次の命題を論理式として評価してみよう。

  1. \(3\)\(2\)より大きい

  2. \(3\)は偶数である

  3. \(\pi\)は3より大きく、\(3.18\)より小さい (from math import pi)

  4. \(121\)\(11\)の倍数でない

  5. 文字"e" は文字列"Hello"に含まれる

6a0111ca86c342348e95c0b72dd6835b

[1]:
3 > 2
[1]:
True
[2]:
3 % 2 == 0
[2]:
False
[3]:
from math import pi
3.0 < pi < 3.18
[3]:
True
[4]:
121 % 11 != 0
[4]:
False
[5]:
"e" in "hello"
[5]:
True

形式論理学

命題の概念は、命題論理につながっていき、 コンピュータ上で論理や思考をプログラミングするとき重要になります。

3.2. 条件分岐

プログラムは、原則、上から順番に[A]→[B]のように評価してゆきます。

code_seq-fs8.png

しかし、条件によっては、評価するプログラムを[X]か[Y]のように切り替えたいことがあります。 そのようなときは、if文と呼ばれる制御構造を用います。

code_if-fs8.png

if文や条件分岐の考え方は難しいものではありません。 しかし、Python には、インデントによってコードブロックを示すなど、 独特なクセがあります。 いくつか書き方のパターンを見て、書き方をマスターしましょう。

重要: インデント(字下げ)

コードの前の空白をインデントといいます。 (Python はインデントの深さでコードブロックの始まりと終わりを表ます。)

3.2.1. 基本形

まず簡単な例題をみながら、しくみと書き方を理解しましょう。

例題(偶数と奇数)

整数値を入力から読み、 偶数の場合はeven, 奇数の場合はoddと表示しよう。

入力

10

出力

even

実際に、入力する値を変えて、結果の変化を試してみましょう。

[6]:
# x = int(input())
x = 10

if x % 2 == 0:
    print("even")
else:
    print("odd")
even

3.2.2. 何も処理しない場合

条件分岐したとき、一方は何も処理しない場合があります。

Python では、「何も実行しない」というプログラムを意味する特殊な文として、 pass文があります。トランプゲームの「パス」という同じです。 だから、print("odd")の代わりにpassにすると、何も実行されません。

[7]:
# x = int(input())
x = 10

if x % 2 == 0:
    print("even")
else:
    pass

even

もうひとつ、else:自体は、何も実行しないときは省略できます。 だから、省略しても構いません。(こちらの書き方が好まれます。

else:を省略

[8]:
# a = int(input())
x = 10

if x % 2 == 0:
    print("even")
even

3.2.3. ブロック:複数行のコードを処理する

分岐処理によっては、分岐先で複数行のプログラムを評価したいことがあります。 同じ深さのインデントが続くときは、コードブロック(つまり複文)として順次、評価されます。

[9]:
# x = int(input())
x = 3

if x % 2 != 0:
    print("***")
    print("***")
    print("***")

***
***
***

ここはインデントによるコードブロックの書き方をしっかり理解しましょう。

コードブロックとインデントの深さ

Pythonは、同じインデントの深さのコードをひとまとまりの処理(コードブロック)とします。 次のように入れ子として書くことができます。

if a != 0:
    print(a)
    if b != 0:
        print(b)
        if c != 0:
            print(c)
        print("b")
    print("a")
print()

3.2.4. 複数の条件があるとき

if文のブロックには、if文を書くことができます。 これにより、より複雑なプログラムが書けるようになります。

例題(複数の条件))

入力1行から空白で区切られた整数値a,bを読みます。 aがbより大きければbig、 等しければsame、 そうでなければ smallと表示しよう。

入力

3 2

出力

big
[10]:
#a, b = map(int, input().split())
a, b = 3, 2

if a > b:
    print("big")
else:
    if a == b:
            print("same")
    else:
            print("small")

big

else:のあとに、すぐに if文が続くときは、elifで書き直すことができます。

[11]:
#a, b = map(int, input().split())
a, b = 3, 2

if a > b:
    print("big")
elif a == b:
    print("same")
else:
    print("small")
big

よくある望ましくない例(あまり効率のよくない書き方)

何度も条件分岐をする

if a > b:
    print("big")
if a == b:
    print("same")
if a < b:
    print("small")

最後がelifで終わっている

if a > b:
    print("big")
elif a == b:
    print("same")
elif a < b:
    print("small")

3.3. 条件式(三項演算子)

Pythonでも、if文を条件式として1行に書くことができます。

<真のときの式> if <論理式> else <偽のときの式>

三項演算子

if式は、C/C++やJavaの三項演算子に相当します。

三項演算子の場合

<論理式> ? <真のときの式> : <偽のときの式>

例題(条件式)

入力1行から空白で区切られた整数値a,bを読みます。 aとbを比較して大きい値を表示しよう。

入力

3 2

出力

3

if式で書いた例

[12]:
#a, b = map(int, input().split())
a, b = 3, 2

print(a if a > b else b)

3

if式(三項演算子)は、ソースコードが冗長になるのを防いでコンパクトに書くことができます。 ぜひ、積極的に利用してみましょう。

if文で書いた例(冗長な書き方)

[13]:
# a, b = map(int, input().split())
a, b = 3, 2

if a > b:
    print(a)
else:
    print(b)
3

3.4. 繰り返し

3.4.1. while文

while文は、特殊な条件分岐の構文になります。 if文の代わりに、while文を用いると、 条件PがTrueの間は``[X]``を繰り返します。

code_while-fs8.png

while文: 条件を満たす限り、繰り返す

[14]:
a = 5
while a > 0:
    print(a)
    a = a - 1
5
4
3
2
1

ちなみに、「if文は条件を満たせば、高々1回実行される」構造でした。

[15]:
a = 5
if a > 0:
    print(a)
    a = a - 1

5

無限ループ

while文の条件がいつまでも真で繰り返しが止まらなくなること

先程の例では、while文の中でa = a - 1が処理されないと、いつまでもa > 0になるため、無限ループになります。

無限ループの例

a = 5
while a > 0:
    print(a)
a = a - 1

無限ループは、頻繁に発生し、主要なバグの原因になります。アプリケーションが反応しなくなる不具合の原因は、ほとんどが「予期しない無限ループに陥っている」と言われています。while文を書くときは、無限ループにならないように注意して書かなければなりません。

防衛的プログラミング( Defensive Programming)

ソフトウェア工学でより詳しく学びますが、 バグや不具合が発生しないようにコードの書き方を選びます。 (動けば良いというのは、20世紀のプログラミングです。)

3.4.2. for文

無限ループは、初学者から上級者まで発生させやすいバグの原因になります。 解説を読んでいるときは、「そんなマヌケなコードは書きませんよ(絶対)」と笑っていても、 結構、頻繁に発生させて凹みます。

無限ループを避ける確実な手段は、while文を使わないことです。 for/in文は、繰り返す回数を指定でき、無限ループを避ける確実な方法です。

code_for-fs8.png

N回繰り返したいとき

for i in range(N):
    print(i) # 繰り返す処理

特別な事情がない場合は、繰り返しはfor文を用いるようにしましょう。 特に、for i in range(N):は、\(N\)回繰り返すときの定番の書き方です。

for 文の変数

変数 iには0から始まる整数が繰り返しの回数分に割り当てられます。 (リストと同じく0 から始まります。

[16]:
for i in range(10):
    print(i, '*' * i)
0
1 *
2 **
3 ***
4 ****
5 *****
6 ******
7 *******
8 ********
9 *********

3.4.3. シミュレーション

シミュレーションは、1ステップずつ値を更新させながら、未来を予測する計算手法です。 プログラミングの得意な分野のひとつとなっています。

例題(お米)

お米は、一粒から1本の苗になります。 苗は、10本の穂を実らせ、各稲穂には60粒のお米が実ります。 つまり、1粒は1年後に600粒になります。

1粒の稲を1億粒以上に増やすには、何年かかるか求めてみよう。

等比数列から計算してもよいですが、シミュレーション(繰り返し)で書いてみましょう。

while文で書いたとき

[17]:
START = 1
GOAL = 1_0000_0000   # _位取り

year = 0
n = START

while n < GOAL:
    n = n * 600
    year = year + 1

print(year)

3

for文で書くときは、少し大きめの繰り返し回数を指定し、 条件を満たしたら、ループを抜けるbreak文を使います。

[18]:
START = 1
GOAL = 1_0000_0000   # _位取り
n = START

for year in range(1, 1000):
    n = n * 600
    if n >= GOAL:
        break
print(year)

3

break

while文やfor文の繰り返しを強制的に抜け出す

3.5. 演習問題

繰り返しは、次回以降、リストや文字列とあわせて練習していきます。

今回は、まず条件分岐に慣れましょう。少し予習で繰り返しも混ざっています。

インデントのエラーに困ったら

Python は、コードをコピペすると、空白記号とタブ記号が混ざって、 インデント周りのエラーが発生しやすくなります。 どうにかなりませんかと言われますが、正直、コピペしないで、 自分でインデントを統一的に書くのが確実です。