条件分岐(後半)

if 〜 else 〜

今日は教科書の Lesson 4 の続きなのですが、教科書ではどういうわけか if 〜 else 〜 を飛ばして、if 〜 elif 〜 else しかありません。初学者の人もいるでしょうから、一応押さえておきます。

下図の左側は前回に出したもので、if 文の条件節が False だった時に、if 文の中に書いた処理を「迂回」する構造です。そして右側が今回紹介する「if 文の条件節が True / False それぞの場合に実行すべき処理を置く構造」です。

image-20231024173000115

True / False で処理を分けるのはとても自然なことと思えます。文法状の記述は以下のようになります。

num1 = int(input())
num2 = int(input())
if num1 > num2:
    print(num1)
else:
    print(num2)

つまり if と同じレベル(インデント位置)に else: を置くと、それ以降が False の場合の処理になります。

if の入れ子

教科書ではなぜか if の入れ子構造(ネスティング)も出てきません。ここで押さえておきます。

例えば三つの数のうち一番大きなものを出す、というアルゴリズムは、このようにも書けます。

image-20231024190304491

赤枠は条件分岐(if 文に相当する)が入れ子構造になっていることを示すために付けてみました。

これに相当する if 文の記述は以下(左側)のようになります。インデントによって一段下がったところに True / False の際の実行文が書かれていますが、そこに if 文があると更にもう一段インデントが進むことがわかるでしょう。

image-20231024190848272

右側のコードは同じものなのですが、入れ子構造になっていることを示すために右側に赤枠をつけたものを置いてみました。一つ上の流れ図の構造・赤枠と対照できますね。

教科書 Lesson 4 続き

これ以降は、教科書の Lesson 4 の続きを進めてください。ただし 4.2, 4.3 だけ、です。それが済めば次の課題に進んでください。

確認課題1. うるう年判定

ある年がうるう年か否かを調べるプログラムを作って下さい。うるう年の定義は以下の通りです。

  1. 4で割り切れる年はうるう年
  2. ただし100で割り切れる年は平年
  3. ただし400で割り切れる年はうるう年

プログラムは実行すると数値を input() で読み取り、その年がうるう年なら “YES”、そうでなければ “NO” を出力するようにしてください。以下のようになるでしょう。

yasuda@Slack 07 % python3 leapyear2.py
2023
NO
yasuda@Slack 07 % python3 leapyear2.py
2024
YES
yasuda@Slack 07 % 

どのように解くか

まず「割り切れるかどうか」は、剰余演算子 % (教科書 p.42 参照)を使うのが簡単です。例えば変数 year の値が 4 で割り切れることを確認するには以下のようにします。

if year % 4 == 0:
    print("割り切れました")

さて、この定義をストレートに流れ図にすると下左図のようになるでしょう。ただ、1, 2, 3 の順をひっくり返して判定すると下右図のようになります。この形は if 〜 elif 〜 else 〜 のパターン(教科書 p.96 参照)ですから、こちらの方が実装したコードはシンプルでわかりやすいものになると想像されます。

image-20231024194834298

また、論理演算子をうまく使えば、以下のような記述で直接的に判定できることがわかるでしょうか。

( year % 4 == 0 ) and ( ( year % 100 != 0 ) or ( year % 400 == 0 ) )

この課題のアルゴリズムや書き方はお任せします。自分にとって扱いやすい方法で作ってください。

動作確認

プログラムがおおよそ動くようになったら、教材 Web にある leapyear.sh シェルスクリプトを取ってきて、以下のようにして実行してください。(前と同じく、中を見て Python プログラムファイルの名前が違っている場合はエディタで leapyear.sh の中を修正して対応してくださいね。)

yasuda@Slack 07 % zsh leapyear.sh
YES
NO
YES
NO
NO
YES
yasuda@Slack 07 % 

結果のパターンが上と同じになっておればひとまず確認としては良いでしょう。Moodle に提出してください。ちゃんとした網羅的なテストについてはまたこの先でやります。

確認課題2. じゃんけん判定

いくつか課題を通して理解度を確認しましょう。

以下のような「じゃんけん」判定プログラムを作成してください。

教材 Web にひな型になるプログラム janken00.py を置いておきました。ダウンロードして中身を確認し、実行してください。以下のようになるでしょう。

yasuda@Slack 07 % python3 janken00.py 
1 
2
win
yasuda@Slack 07 % 

このプログラムは(中を見るとわかりますが)変数 me, you にデータを読み込み、無条件に “win” と出力します。あなたは正しく勝ち負け判定ができるようにコードを追加し、このプログラムを完成させてください。

どのようにして解くか

つまりアルゴリズムをどうするか、はなかなか考え所だと思います。すべての入力の組み合わせ(一人が出す手が 3 通りで、それが二人ですから 3*3 の 9 通りです。単純に考えられるアルゴリズムを列挙してみます。

  1. 【下左図】すべての入力 9 通りに対して個別に反応する 9 つの if 文を並べ、すべてを if 〜 elif 〜 elif … 〜 else 〜 で連結する(それぞれの条件判定は自分と相手の手の判定を論理積演算子でつないで判断することになるか)
  2. 【下中図】勝ち、負け、あいこの 3 通りの結果に対し、どの手が当てはまるか調べて、if 〜 elif 〜 else 〜 で分岐させる(それぞれの条件判定は 3 条件を論理和演算子でつないで判断することになるか)
  3. 【下右図】勝ち負けを循環する関係性から判断する

image-20231024181332317

様々な方法があると思います。

あたりのことに気をつけて作業してください。

動作確認

プログラムがおおよそ動くようになったら、教材 Web にある janken.sh シェルスクリプトを取ってきて、以下のようにして実行してください。(これのも必要なら janken.sh の中を修正して対応してください。)

yasuda@Slack 07 % zsh janken.sh 
even
even
even
win
win
win
lose
lose
lose
yasuda@Slack 07 % 

これで結果が even, win, lose の順で3回ずつ表示されれば判定処理は正しくできています。Moodle に提出してください。

いろいろとエレガントな方法があり得ます。これが良い、と思えるアルゴリズムと記述で提出してください。

参考:実数処理における誤差

実数の判定処理については誤差を意識する必要があります。実数処理における誤差 を見て下さい。

参考:長すぎる行を折り返して書く

うるう年判定のところで出した論理演算子を用いた条件式の例を出しました。

( year % 4 == 0 ) and ( ( year % 100 != 0 ) or ( year % 400 == 0 ) )

記述が長くなった場合は、適度に折り返してコードを書いておいた方が分かりやすい場合があります。p.19 にもありましたが、「  (バックスラッシュ)」を行末に置くことで次の行に続いていることを示します。以下に例を出します。

if ( year % 4 == 0 ) and ( ( year % 100 != 0 ) or ( year % 400 == 0 ) ):
   ↓↓↓↓↓ 下のように折り返して書ける
if ( year % 4 == 0 ) and \
   ( ( year % 100 != 0 ) or ( year % 400 == 0 ) ):

バックスラッシュを使って折り返した次の行のインデント位置はどこでも構いません。

# 上の例よりは大きくインデントをつけてみた。これでも良い。
if ( year % 4 == 0 ) and \
        ( ( year % 100 != 0 ) or ( year % 400 == 0 ) ):

# 隙間を空けず左端から書いても問題はない。見づらいが。。
if ( year % 4 == 0 ) and \
( ( year % 100 != 0 ) or ( year % 400 == 0 ) ):

バックスラッシュで分割した二行を単純につないで解釈されるだけですから、上の例でいくと「and」と、その後の空白一つ、次の行の先頭の空白三つで「 ( 」です。下に接続し直した状態を示します。

if ( year % 4 == 0 ) and \
   ( ( year % 100 != 0 ) or ( year % 400 == 0 ) ):
   ↓↓↓↓↓ 下のように接続した状態で解釈される
if ( year % 4 == 0 ) and    ( ( year % 100 != 0 ) or ( year % 400 == 0 ) ):