第2回 本当に占いをする

3.本当に占いをする

では、次に、毎回大吉ではなく、占いらしく、毎回結果を変えてみましょう。
今回は乱数を使って、結果を変化させていきます。

◼️要件

前回作ったプログラムの表示結果をランダムで

  • おめでとうございます!大吉です
  • セーフ!吉です
  • がーん… 大凶です

のいずれかを表示するように変更します。

◼️ソースコード

import random
print('あなたの今日の運勢は')
for i in range(10):
    print('・')
  
fortune = random.randint(0,2)
if fortune == 0:
    print('おめでとうございます!大吉です')
elif fortune == 1:
    print('セーフ!吉です')
else:
    print('がーん… 大凶です')

実行結果

実行結果
あなたの今日の運勢は
・
・
・
・
・
・
・
・
・
・
セーフ!吉です

◼️解説

0. このプログラムを理解するのに必要な知識
  1. import文で追加モジュールを使えるようになる
  2. random.randint関数でランダムな整数を生成できる
  3. = で値を変数に保存できる
  4. if 条件 elif 条件 elseで条件分岐できる
  5. (ちょっと詳しく)値を返すということ
1. 追加モジュールの利用

占いの結果を毎回違うものにするという要件に対して、今回は乱数を用います。
pythonの組み込み関数には乱数を生成する関数は含まれておらず、randomモジュールの中に定義されています。モジュールとは、関数の集まりで、読み込むことで利用できる関数が拡張されます。
randomモジュールは標準ライブラリなので*1、すぐに読み込み・利用が可能です。
モジュールの読み込みにはimport文を使います。

import random

これで、randomモジュールに含まれる乱数生成関数が利用可能になります。

2. 乱数の生成

今回はランダムな整数(小数ではなく)を生成しましょう。

random.randint(最小値, 最大値)

で指定範囲のランダムな整数を生成します。このことを「関数が値を返す」といい、返される値のことを「戻り値」といいます。また、上述の最小値や最大値のような関数のカッコ「( ) 」の中に記述するパラメータのことを「引数」といいます。*2

print(random.randint(1, 10))
実行結果
 7

(結果は1から10の間の整数でランダムです)
感覚的に生成された乱数を表示している様子が分かると思いますが、randint関数とprint関数が処理されていますので、きちんとした解説は次で行います。

3. 変数と代入

生成した乱数は、その後占いメッセージの決定に使いますから、保存しておきましょう。値を保存する入れ物のことを「変数」といいます。
f:id:inato_gen:20190210191812j:plain

変数に値を保存するには「=」記号を使います。

変数 = 値

変数はプログラム中に記述することで、格納されている値として評価(参照)されます。

x = 100
print(x)
実行結果
 100

print(x)が実行される瞬間(プログラムは上から1行ずつ実行されることを思い出してください)には変数xの値(内容)は100なので、このような実行結果になります。
今回のプログラムでは生成した乱数を変数に保存するので

fortune = random.randint(0, 2)

のようなプログラムになります。
変数名はおみくじということで「fortune」にしましたが、これはどんな名前でも構いません。omikujiでもxでもaaaaでも大丈夫です。自分で分かりやすい名前をつけましょう。*3

javaなど他の言語では使用する変数名を先に宣言したり、格納できる値の種類(「型」といいます)を宣言したりするものもありますが、pythonでは必要ありません。ただし、スペルミスしても自動的に変数と扱われるため注意が必要です。

fortune = 1
fotune = 2
↑変数名を間違えている
print(fortune)
実行結果
 1

4. 条件分岐

乱数が生成できたので、その数字によってメッセージを変えていきます。
実装方法にはいくつか考えられますが、今回はシンプルに乱数の値によって処理を振り分けてみます。
乱数の値は0から2、つまり0、1、2の3種類なのでfortuneが0のとき、1のとき、それ以外(つまり2のとき)の処理を記述します。
特定の条件に合致したときのみ実行する処理を実現するにはif文を使います。

if 条件1:
    条件1が成立した場合の処理
elif 条件2:
    条件2が成立した場合の処理
else:
    どの条件にも合致しなかったときの処理

条件から処理までの組を「節」といいます。上の例では、if節、elif節、else節から構成されていますが、elif節やelse節は省略が可能です。また、elif節は必要に応じて複数記述することができます。

さらに今回「処理」とした部分には複数行のコードを記述できます。このひとかたまりを「コードブロック(または単にブロック)」といい、pythonでは連続する同じインデントの行が同じブロックと認識されます。インデント…前回のfor文でも登場しましたね!for文も繰り返し範囲をブロックで表現していたんです。

今回のプログラムではfortune == 0のような式が書かれていますが、この比較式が成立するとき次に続く処理が実行され、不成立のときにはスキップされ次の節に進みます。

5. 「値を返す」ということ

変数、関数はプログラム固有の概念なのでここでつまずく方も多いと思います。また、for文やif文も丸暗記しても少し複雑になるとわからなくなります。
構成要素ごとに分けて考えることで、シンプルかつ応用が利くように理解しましょう。

①変数の評価
変数はプログラム中に記述すると、実行時に格納されている値として評価されますが、これは変数部分を値に置き換えたコードと等価なものとして扱われると考えることで分かりやすくなると思います。
xに100が格納されているとして、以下のコード

print(x)

は実行時に以下のコードに展開されている、と考えることができます。

print(100)

②関数の戻り値
関数が値を返すということも同じように置き換えで理解することができます。
randintが2を返したとして以下のコード

x = randint(1, 10)

は、以下のコードと等価に扱われているということです。

x = 2

演算子の戻り値
==のような「演算子」も値を返します。
比較演算子の場合、成り立つときTrue、成り立たないときFalseを返します。この値の種類を「真偽値」(Trueが真で、Falseが偽です)といいます。
fortuneが0だとして

if fortune == 0:

はfortune == 0が成り立つので、Trueを返します。「返す」ということは置き換えたコードと等価ですので

if True:

ということになります。
つまり、if文とはif True:になれば続くコードが実行され、if False:になればスキップするということです。

その他の演算子、たとえば四則演算子は計算した結果を「返し」ます。以上のことを踏まえると

x = 100
print(x + random.randint(1, 10))

の2行目は以下の順に展開されていることになります。

# randint関数を実行(仮に5を返したとする)
print(x + 5)
# 変数xを展開
print(100 + 5)
# +演算子を評価
print(105)
実行結果
 105

プログラミングではそれぞれの要素(変数や関数や演算子など)はどのようなシチュエーションで使っても動作が変わるわけではないので*4、分けて考える能力は非常に大事だと思います。

◼️その他の実現方式

今回は占い(おみくじ?)の結果を変動させるために乱数を用いましたが、この辺りの実現方法はさまざまなことが考えられます。例えば、実行した時刻を用いると実際にはランダムではありませんが、十分ランダムのように見えると思います。
また、完全にランダムでは不自然だということであれば、日付を使って同じ日の間は同じ結果にするなど工夫することになるでしょう。
本当の占いアプリを作るのであれば、実際の占いと同じロジックを実装することになります。

*1:標準「ライブラリ」にrandom「モジュール」が含まれるという不思議ですが、pythonドキュメントでそう呼ばれているので従います

*2:「パラメータ」でもいいと思いますが

*3:変数に使える名前には決まりがありますが、今はあまり気にしなくていいでしょう

*4:言語や構文によっては一部例外があります…