ここでは、前もって「当たりの数値」を決めておき、ユーザーがそれを当てるまでの間、ユーザーの入力を受け付けて、それが「当たり」か「外れ」かを判定し続ける数当てゲームを何種類かの方法で書きながら、while文やif文とその条件(比較演算子の使い方)などを見ていこう。
ただし、その前にプログラムの大枠について簡単に見ておく(まだ説明していない要素もあるので、それについては「そんなものか」と読み飛ばしてほしい)。ここでは、コメントとして大枠を書いておいて、後からそれを実際のコードに落とし込んでいこう。「当たりの数値」は1〜100の範囲に含まれる整数値とする。
# 乱数を使うための準備をする
# 乱数を使って、「当たりの数値」を前もって決めておく
# ユーザーから最初の入力を受け付ける
# ユーザーが「当たりの数値」を入力するまで以下を行う:
# 入力された値が「当たりの数値」より大きいか小さいかを
# 表示して、もう一度ユーザーから入力を受け付ける
# 「繰り返し処理が終了」=「当たりの数値が入力された」となるので祝福する
最初の2行が本連載ではまだ取り上げていない事項だ。「乱数を使うための準備」とは「Pythonには標準で組み込まれていない乱数機能を使うよ」とPythonに教えてやることだ。これには「import文」が使える。import文を使うと、Pythonには標準では備わっていない「機能」を取り込んで使えるようになる。今回は「乱数」を扱うための「randomモジュール」を利用する。randomモジュールを取り込むには「import random」と書くだけだ(randomモジュールはPythonと一緒に配布される「標準ライブラリ」に含まれているので、これを書くだけですぐに使える)。
次の行の「当たりの数値」を前もって決めるには、このモジュールが提供するrandint関数を使う。この関数は2つの引数aとbを受け取ると、「a以上、b以下」の範囲に収まるランダムな整数値を返してくれる。これを変数answerに保存しておこう。上でも述べたが、「当たりの数値」は「1〜100の範囲に収まる整数値」とすると、最初の2行は次のようになる。
import random
answer = random.randint(1, 100)
# ユーザーから最初の入力を受け付ける
# ユーザーが「当たりの数値」を入力するまで以下を行う:
# 入力された値が「当たりの数値」より大きいか小さいかを
# 表示して、もう一度ユーザーから入力を受け付ける
# 「繰り返し処理が終了」=「当たりの数値が入力された」となるので祝福する
ちょっと注目してほしいのは、「ユーザーから最初の入力を受け付ける」という部分だ。その下の3行がwhile文で行う処理となるが、そこにたどり着く前に一度はユーザーに数値を入力してもらわないと、while文で「ユーザーが当たりの数値を入力したか」の判定ができないので、このようになっている(二度目以降のユーザー入力の受け付けはwhile文の中で行う)。「while文で処理を行う前に、必要な初期情報をセットアップしておく」のはよくあるパターンなので覚えておこう。
そして、「ユーザーが『当たりの数値』を入力するまでは以下を行う:」とその下の2行がwhile文を使って繰り返し処理を行っていく部分だ。なお、while文が終わるのは、ユーザーが「当たりの数値」を入力したときなので、その後で祝福のメッセージを表示すればよい。では、実際のコードを考えていこう*2。
*2 第2回「Hello Python」の「もう少し難しいHello Worldプログラム」で触れているように、「Try Jupyter」ページの「Jupyter Notebook」リンクから起動するPython環境では以下のコードがうまく動作しない。対処策としては上記ページにあるように、Binderを使う方法がある。以下では、Binderを使って起動した環境で話を続けることにしよう。ただし、こちらの環境ではinput関数を呼び出すと、以下のようにダイアログが表示されて、プログラムの出力が隠されてしまう。
この場合は、ダイアログの外側をクリックすれば、セルの下部に入力ボックスが表示されるので、そこに数を入力すればよい。
では、上記のコードではどのような形で繰り返し処理を行うかを考えてみる。すると、次のような手順が考えられる。
手順1と手順3の処理にあるユーザー入力の受け付けについては、input関数でユーザーから受け取った文字列をint関数で整数値に変換したものを変数numberにとっておけばよいだろう(第4回の「文字列と数値の変換:str/int/float関数」を参照)。よって、上の手順を疑似的なコードにまとめると次のようになる。
import random
answer = random.randint(1, 100)
number = int(input('100までの数値を入力してください: ')) # 手順1
while ユーザーが当たりの数値を入力するまで: # 手順2
# 手順3
# 変数numberの値と変数answerの値の大小関係を表示
number = int(input('100までの数値を入力してください: '))
print('素晴らしい! 正解です!')
ところで、手順2の「ユーザーが当たりの数値を入力するまで」を表現するコードはどんなものだろう。「ユーザーが当たりの数値を入力するまで」とは、「ユーザーが入力した値と当たりの数値が同じになるまで」ともいえる。これは「ユーザーが入力した値と、当たりの数値が等しくない間」は「手順3を繰り返し実行する」とも読み替えられる。ユーザーが入力した値は変数numberに、当たりの数値は変数answerに保存されているので、これを比較すればよいということだ。
「2つの値が等しくない」ことを調べるには「比較演算子」のうち、「!=」演算子を使う。これは演算子の左右の値が等しくないときにはTrueが演算結果として、等しいときにはFalseが演算結果とするものだ。よって、while文の「条件」は「answer != number」または「number != answer」と書ける。
import random
answer = random.randint(1, 100)
number = int(input('100までの数値を入力してください: ')) # 手順1
while answer != number: # 手順2
# 手順3
# 変数numberの値と変数answerの値の大小関係を表示
number = int(input('100までの数値を入力してください: '))
print('素晴らしい! 正解です!')
手順3では、変数answerの値と変数numberの値を比較して、前者が小さければ「小さい」と表示して、そうでなければ「大きい」と表示して、もう一度ユーザーから入力を受け付ければよい。この比較にはif文を使えばよいだろう。if文の条件には、やはり比較演算子の「<」演算子または「>」演算子が使える。
「<」演算子は左側の値(被演算子)が右側の値(被演算子)よりも「小さい」ときにはTrueを演算結果として、そうでなければFalseを演算結果とする。「>」演算子はその逆だ。よって、「<」演算子を使えば、「answer < number」の値がTrueなら「小さい」と表示して、そうでなければ「大きい」と表示すればよい。よって、コードは次のようになる(大小関係を伝えるメッセージは少し丁寧な表現にしてある。
import random
answer = random.randint(1, 100)
number = int(input('100までの数値を入力してください: '))
while answer != number:
if answer < number:
print('もっと小さな数値です')
else:
print('もっと大きな数値です')
number = int(input('100までの数値を入力してください: '))
print('素晴らしい! 正解です!')
最初のバージョンのプログラムはこれで完成だ。実際に実行してみよう。
ここで幾つか考慮すべき点について述べておこう。まず、ユーザーが最初から当たりの数値を入力したときの動作について考えてみる。このときには、while文の条件である「answer != number」が成り立たない。よって、繰り返し処理は一度も実行されない。また、if文の中では「<」演算子を使っているが、その条件分けで「変数answerの値と変数numberの値が等しい場合」を考慮していないようにも見える。しかし、if文が実行されるのはそもそも「これら2つの変数の値が等しくない場合」なので問題はない。それから、if文の条件で「>」演算子を使うとコードがどうなるかだ。これについては自分で考えてみよう。
次に、上記のコードでwhile文の条件を別のものにしてみよう(演習問題のようなものだ)。
先ほどは数当てゲームの大枠として次のようなものを見てもらった。
# 乱数を使うための準備をする
# 乱数を使って、「当たりの数値」を前もって決めておく
# ユーザーから最初の入力を受け付ける
# ユーザーが「当たりの数値」を入力するまで以下を行う:
# 入力された値が「当たりの数値」より大きいか小さいかを
# 表示して、もう一度ユーザーから入力を受け付ける
# 「繰り返し処理が終了」=「当たりの数値が入力された」となるので祝福する
この中で「ユーザーから最初の入力を受け付ける」のが「美しくない」と感じる人もいるかもしれない(プログラミングに慣れてくると、それがよくあるパターンと感じられるようにもなるが)。そこでwhile文の条件を変えて、この行をなくしてみよう。
これには「無限ループ」と呼ばれるものを使う。「無限ループ」とはもちろん、「ループ(繰り返し処理)が無限に続く」ことを意味した言葉だ。while文は「条件が真である間」は繰り返し処理を続ける。そこで「条件」にブール値のTrueをそのまま書いてみるとどうなるだろう。
while True:
ブロック
こうすると、while文に記述したブロックの内容が永遠に実行され続ける。では、プログラムは永遠に終わらないかというとそうでもない。前回紹介した「break文」は「それを囲んでいる最も内側のループを終了させる」ものだった(for文でもwhile文でも利用可能。continue文も同様だ)。これを使えば、無限ループの中で何らかの条件判断を行って、条件が成立したときには、それを終了させられる。これを使って先ほどの大枠を書き換えたものが以下だ。
# 乱数を使うための準備をする
# 乱数を使って、「当たりの数値」を前もって決めておく
# ユーザーが当たりの数値を入力するまで無限ループ:
# ユーザーから入力を受け付ける
# 当たりの数値 < ユーザー入力なら「小さい」と表示
# 当たりの数値 > ユーザー入力なら「大きい」と表示
# 当たりの数値 == ユーザー入力なら無限ループを終了
# 「繰り返し処理が終了」=「当たりの数値が入力された」となるので祝福する
「ユーザーが当たりの数値を入力するまで無限ループ:」は既に述べた通り「while True:」と表現できる。その下の「ユーザーからの入力を受け付ける」も既に見た。その下の条件分岐にはif〜elif〜else文を使えばよいだろう。これを実際にコードにすると次のようになる(実行結果は省略)。
import random
answer = random.randint(1, 100)
while True: # 無限ループ
number = int(input('100までの数値を入力してください: '))
if answer < number:
print('もっと小さな数値です')
elif answer > number:
print('もっと大きな数値です')
else:
break # 変数answerの値と変数numberの値が等しければ終了
print('素晴らしい! 正解です!')
バージョン1とバージョン2のどちらかがよくて、どちらかが悪いというものではなく、while文やif文では「同じ条件でも表現の方法が1つだけとは限らない」ということだ。複数の条件をand演算子やor演算子で繋いで記述するのが正解なこともあれば(第9回の「if文の条件と比較演算子とブール演算子」を参照)、頭の中で条件を整理してシンプルに記述する方がよいこともある。条件の真偽を反転させた方が分かりやすいコードになることもある。その時々に応じて、分かりやすい条件を書くように心がけよう。
Python 3.8以降では、代入式と呼ばれる式が使える。代入式では「変数 := 式」のように「:=」演算子を使って、式の値を変数に代入する。式なので、これを実行すると代入された値がその式の値となる。
while文の条件に書けるのは、if文の条件と同じく、何かの値を返す「式」だ。その式の値を評価(計算)して、その値がTrueとFalseのどちらになるかによって、while文のブロックを実行するのか、ループを終了するのかが決まる。ここには「変数 = 式」のような代入文を書けないが、上述の「変数 := 式」という代入式を使うことでwhile文の条件として「変数に何かの値を代入した上で、その値を基にTrueかFalseの判断を行う」ことが可能となる。
バージョン1のコードはおおよそ次のような処理を行っていた。
import random
answer = random.randint(1, 100)
number = int(input('100までの数値を入力してください: ')) # 手順1
while answer != number: # 手順2
# 手順3
# 変数numberの値と変数answerの値の大小関係を表示
number = int(input('100までの数値を入力してください: '))
print('素晴らしい! 正解です!')
ここで手順1と手順2が別々の行になっているのは、まさに上で述べたような「何かの値を変数に代入すると同時にその値を使って真偽の判定をする」ことが無理だったからだ。代入式を使うと上の擬似コードは次のようにまとめられる(手順3のコードも含めて完成形とした)。
import random
answer = random.randint(1, 100)
while (number := int(input('100までの数値を入力してください: '))) != answer:
if answer < number:
print('もっと小さな数値です')
else:
print('もっと大きな数値です')
print('素晴らしい! 正解です!')
while文の冒頭で、代入式を使って「number := int(input(……))」のようにして、変数numberにinput関数の戻り値(ユーザーが入力した数)を整数にしたものを代入し、その値が変数answerの値と正しくないことを比較していることに注目されたい。
さらに、ここでinput関数を呼び出すようにしたことで、while文のブロックで実行するコードからはinput関数の呼び出しがなくなったことにも注意しよう。最初に入力を受け付けて、その後でwhileループで入力を受け付けるのが美しくないと感じる方には、こうした記述方法はシンプルに感じられるはずだ(ただし、代入式の部分のコードが長いのは気になるかもしれない)。
このように、何かの値を入力したり、Webからデータを受け取ったり、ファイルやデータベースから読み込みをしたりして、その結果を基にwhileループを実行するときには、代入式を使うことでコードがシンプルになるかもしれない。
今回はwhile文を紹介した。数値/文字列などの基本的なデータや変数、各種の演算子と、今回までに3回に分けて紹介してきたif文/for文/while文を組み合わせることで、より高度なプログラムを記述できるようになる。次回は、Pythonの関数について見ていく。
「Python入門」
Copyright© Digital Advantage Corp. All Rights Reserved.