8. 3大構造 三. 反復

この章では、プログラムの3大構造の3つ目「反復(はんぷく)」を学びます。 反復とは「繰り返し」です。

反復には2つのパターンがあります。

  1. 決まった回数、ある処理を繰り返す

  2. 条件を満たす限り、ある処理を繰り返す

どちらも、ふだんの生活に例があります。 1の例は、「知人1人1人について、住所と宛名を変えながら年賀状を印刷する」ケースです。 知人の人数だけ繰り返します。 2の例は、ゲームで「ボスのHP(ヒットポイント)が0になるまで攻撃を繰り返す」ケースです。

反復を実現するのに、次のものを使います。

  1. 決まった回数、ある処理を繰り返す → for 文と、複数の値を扱う仕組み

  2. 条件を満たす限り、ある処理を繰り返す → while

順番に見ていきましょう。

8.1. for 文を使って、決まった回数繰り返す

「決まった回数、ある処理を繰り返す」のに使うのは、次の2つです。

  • リスト

  • for

8.1.1. リスト

リストの説明は、反復に必要な範囲の説明に留めています。 次の 9 章複数の値をまとめて扱おう」で詳しく紹介します。

リストは、複数の値をまとめた値 です。 数値や文字列は1つの値でした。 複数の数値や文字列をまとめるのに、リストを使います。

リストは複数の値を [] で囲んで書きます。 複数の値はカンマ(,)で区切って [] の中に並べます。

対話モードで、リストを作ってみましょう。 複数の数値 123 をまとめて扱うリストです。

>>> [1, 2, 3]
[1, 2, 3]

続いて、複数の文字列をまとめて扱うリストです。

>>> ["吾輩は猫である", "坊っちゃん"]
['吾輩は猫である', '坊っちゃん']

[コラム] リストでカンマの後ろに半角スペースを入れるのはなぜ?

リストにまとめた値を区切るカンマの後ろに半角スペースを入れなくても、できあがるリストに違いはありません。

>>> [1,2,3]
[1, 2, 3]

プログラムを読みやすくするため に、半角スペースが入れられます。 本書でもカンマの後ろに半角スペースを入れます。

8.1.2. for

for 文も文の1種で、別の文を含められる文です。 以下のように書きます。

for 変数 in リスト:  # ((半角スペースが1つずつ入ります。また、コロンを忘れずに!))
    処理(文(の並び))  # ((先頭のインデントを忘れずに!))

リストにまとめられた値を 1つ1つ変数に代入し、処理を繰り返し ます。

処理は何行でも(1行だけでも100行でも)書けますが、インデントを揃える必要があります (1行でもインデントが異なると IndentationError となります)。

リストにまとめられた値が、for 文で変数に代入される様子を例で見てみましょう。

for i in [1, 2, 3]:
    変数iを使った処理

リストでは、左が先頭で右が末尾と、左から右 の順で値が並んでいます。 [1, 2, 3] の場合は、1 が先頭で、2 がその次、3 が末尾です。

for 文で変数に代入するときは、リストにまとめられた値の順番に従います。 リストの先頭からはじめて、次の値、次の値 と代入されます。

  1. まず i1 を代入して処理を行います。

  2. 続いて i に次の 2 を代入して処理を行い、

  3. さらに i に次の 3 を代入して処理を行います。

処理の中で変数 i を評価すると、指す値がリストの順番に沿って変わるのです。

リストの末尾に達し、変数に代入する次の値がなくなったらfor 文に続く処理に進みます。

Atomで renshu_for.py というファイルを作りましょう。

renshu_for.py
1print("for文の練習プログラムを開始します")
2for i in [1, 2, 3]:
3    print(i)
4print("プログラムを終了します")

ファイルを実行して、処理系に for 文を実行させましょう。

> python renshu_for.py

macOSでは読み替えが必要です。 詳しくは 6 章 の「はじめてのファイルを使ったプログラム」を確認してください。

Enter キーを押して実行すると、リストの順番に従って、1、2、3と3行出力されます。

> python renshu_for.py
for文の練習プログラムを開始します
1
2
3
プログラムを終了します

リストの先頭から末尾へと繰り返す様子をつかんでください。

renshu_for.py における繰り返し

i の指す値

for の処理(print(i)

1 (先頭)

1 を表示

2 (次の値)

2 を表示

3 (次の値)

3 を表示

(リストの末尾に達していて、次の値はない)

for 文の後の4行目に進む)

../_images/9-22_for_1.drawio.png

i1 を指す状態で、処理(3行目)を実行

../_images/9-23_for_2.drawio.png

再度 for の行(2行目)に戻り、i が次の 2 を指す状態で、処理を実行

../_images/9-24_for_3.drawio.png

三度 for の行に戻り、i が次の 3 を指す状態で、処理を実行

../_images/9-25_for_4.drawio.png

for の行に戻るが、リストに次の値がなくなったので、for 文の次の文に進む

print 関数を使って、リストの中身を表示する例を紹介しました。 リストと for 文を使って、リストにまとめられた値それぞれに対して1回ずつ処理を実行 できます。

8.1.3. 練習問題

筆者の名前のリストを使って、本書の筆者全員に1回ずつ挨拶しましょう。

筆者の名前のリストは ["susumuis", "nao_y", "nikkie"] とします。 挨拶する部分は、これまで書いてきたプログラムにならって、「〇〇さん、ごきげんよう」と表示しましょう(〇〇には筆者の名前が入ります)。 ファイル renshu_for.py を編集してください。

ヒント:筆者の名前のリストの中身を表示するには、次のようにすればいいですね。

renshu_for.py(練習問題のヒント)
1for hissha in ["susumuis", "nao_y", "nikkie"]:
2    print(hissha)

あとは表示内容を「(筆者の名前)さん、ごきげんよう」と変えるだけです。

[解答]

renshu_for.py
1for hissha in ["susumuis", "nao_y", "nikkie"]:
2    print(f"{hissha}さん、ごきげんよう")

実行すると、筆者1人1人に挨拶します。

> python renshu_for.py
susumuisさん、ごきげんよう
nao_yさん、ごきげんよう
nikkieさん、ごきげんよう
変更した renshu_for.py における繰り返し

hissha の指す値

for の処理

"susumuis" (先頭)

"susumuis" への挨拶を表示

"nao_y" (次の値)

"nao_y" への挨拶を表示

"nikkie" (次の値)

"nikkie" への挨拶を表示

(リストの末尾に達していて、次の値はない)

for 文の後の文へ進む)

[発展] 別の反復処理を繰り返す

for 文の処理には、for 文も書けます。 外側の for 文で代入した変数を使って、別の処理を繰り返せ ます。

例として、前編/後編がある4巻の書名を全て表示してみます。

renshu_for.py
1print("for文の練習プログラムを開始します")
2for kan in ["杯", "騎士団", "王子様", "秘宝"]:
3    for hen in ["前編", "後編"]:
4        print(f"『{kan}-{hen}』")
5print("プログラムを終了します")

処理系がプログラムをどう実行するか考えてみましょう。

まず、変数 kan"杯" を指した状態で、3,4行目が実行されます。 前編、後編の両方について、書名の表示を繰り返します。

その後、kan が指す値は "騎士団""王子様" と順番に変わります。 それぞれの値について、2つの編の書名の表示が繰り返されます。

> python renshu_for.py
for文の練習プログラムを開始します
『杯-前編』
『杯-後編』
『騎士団-前編』
『騎士団-後編』
『王子様-前編』
『王子様-後編』
『秘宝-前編』
『秘宝-後編』
プログラムを終了します

8.2. while 文を使って、条件を満たす限り繰り返す

「条件を満たす限り、ある処理を繰り返す」には、while 文を使います。

8.2.1. while

while 文も文の1種で、別の文を含められる文です。 以下のように書きます。

while 式:  # ((半角スペースが1つずつ入ります。また、コロンを忘れずに!))
    処理(文(の並び))  # ((先頭のインデントを忘れずに!))

式を評価 して True となる限り、処理を実行 します。

while に続く式は、条件 を表します。 この式が True と評価されるとき、処理を順次実行します。 処理の中で、条件に使われる変数が更新されます。 処理を実行し終わったら、再度条件を評価 します。

  • 条件が True と評価されたら、再び処理を繰り返し、その後、条件を評価します。

  • 条件が False と評価されたら、そこで while 文の繰り返しは終了し、続く文を実行します。

このように、条件が True と評価される、すなわち、条件を満たす限り、処理が繰り返し実行されます。

処理の書き方は for 文における処理の書き方と同じです。 IndentationError を防ぐため、処理のインデントは揃えてください。

Atomで renshu_while.py というファイルを作って、練習しましょう。

renshu_while.py
1print("while文でオウム返しします")
2nyuryoku = input("入力してください(qで終了) ")
3while nyuryoku != "q":
4    print(nyuryoku)
5    nyuryoku = input("入力してください(qで終了) ")
6print("プログラムを終了します")

入力と同じ文字列を表示する練習プログラムを用意しました。

このプログラムでは、比較演算子で文字列を比較しています。 文字列の比較については、「3大構造 二. 分岐」の発展(TODO:Xページ参照)を確認してください。

以下は実行の一例です。

> python renshu_while.py
while文でオウム返しします
入力してください(qで終了) ごきげんよう
ごきげんよう
入力してください(qで終了) さしもしらじなもゆるおもひを
さしもしらじなもゆるおもひを
入力してください(qで終了) q
プログラムを終了します

プログラム2行目の input 関数で入力を受け取ります。 今回は「ごきげんよう」と入力しました。 この文字列は "q" と一致しないので、while 文の処理が実行されます。 入力内容が出力され、再度入力を受け取ります。

今度は「さしもしらじなもゆるおもひを」と入力しました。 これも "q" と一致しないので、オウム返しをしてから、入力を受け取ります。

「q」と入力すると、while の条件は False となり、続く処理(プログラム6行目)に進みます。

renshu_while.py 実行の一例

nyuryoku の指す値

while の条件 nyuryoku != "q"

while の処理が行われるか

while の処理

"ごきげんよう"

True

"ごきげんよう" を表示。入力を受け取る

"さしもしらじなもゆるおもひを"

True

"さしもしらじなもゆるおもひを" を表示。入力を受け取る

"q"

False

×

実行されない

なお、最初に入力した「ごきげんよう」に代えて「q」と入力すると、 while 文の条件が成り立たないため、処理は一度も実行されずに、プログラムは終了します。

../_images/9-26_while_1.drawio.png

「ごきげんよう」という入力を受け付けたとき、while の行の条件が成り立つので、処理(4, 5行目)を実行する

../_images/9-27_while_2.drawio.png

「さしもしらじなもゆるおもひを」という入力を受け付けた後、while の行に戻る。条件が成り立つので、処理を実行する

../_images/9-28_while_3.drawio.png

「q」という入力を受け付けた後、while の行に戻る。条件が成り立たないので、while 文の次の文に進む

while 文を見たら、条件が成り立たないのはどんなときか 考えましょう。 while 文の繰り返しが止まる状況をイメージできると、他のプログラマーが書いた反復のプログラムも読みやすいでしょう。

8.2.2. 練習問題

7 章3大構造 二. 分岐」の練習問題で作った計算アプリを、正解するまで入力を受け付けるように変更してみましょう。 惜しい間違いのフィードバック機能は一度なくして考えてみましょう。

ヒント:間違え続ける限り、入力を受け付けます。

[解答]

想定解を示します。 表示する文面が異なっていても、間違え続ける限り、入力を受け付けるプログラムになっていれば、正解です。

keisan.py
1print("計算アプリです")
2nyuryoku = input("11×12は? ")
3seikai = 11 * 12
4while int(nyuryoku) != seikai:
5    nyuryoku = input("違います。諦めずに答えてみましょう ")
6print("正解です!")

while 文を使って、正解が入力されない限り繰り返しました。

> python keisan.py
計算アプリです
11×12は? 122
違います。諦めずに答えてみましょう 132
正解です!

while 文の処理を工夫すると、惜しい間違いのフィードバック機能も持たせられます。

反復は、プログラムに処理の を作っていると見なせます。

for のリストに次の値がある場合や、while の条件が True となる場合、 輪が1周回って元の行に戻り、リストの次の値や条件が確認されます。 for のリストに続く値がない場合や、while の条件が False となる場合、そこで輪が切れます。

[発展] 反復を途中で終了する break

プログラミングには、反復を途中で終了 する仕組みも用意されています。 その仕組みは break 文と呼ばれます。 break 文を使うと、オウム返しのプログラムは以下のようにも書けます。

renshu_while.py
1print("while文でオウム返しします")
2while True:
3    nyuryoku = input("入力してください(qで終了) ")
4    if nyuryoku == "q":
5        break
6    print(nyuryoku)
7print("プログラムを終了します")

変更点は2箇所あります。

  • while 文の式を True としました。毎回処理を実行します。

  • 処理の中に分岐があります。nyuryoku が指す値(入力値)が "q" と一致するとき break 文を実行します。

break 文が実行されると、while 文の実行は そこで終了 し、続く文を実行します。

条件は常に成り立ちますが、入力値によって処理を終了する分岐があるので、反復はどこかで終了します。 break 文を使って書き換えて、これまでと同じように動くことを確認してみてください。

[発展] 無限ループを止める

while 文を書き間違えて、際限なく反復し続けるプログラムを書いてしまうことがあります。

  • 条件に使う変数の更新忘れ

  • break 文の分岐の書き忘れ

実は、プログラムを実行している処理系は、プログラマーの キーボード操作で終了 させられます。 2つのキー [Ctrl] と [C] を同時に押してください(WindowsでもmacOSでも共通のキー操作です)。 これを使えば、際限なく反復するプログラムを実行する処理系も止められます。 この2つのキーを押すことで、処理系の実行に 割り込んで います。

なお、際限のない反復を、プログラマーは「無限ループ」とも呼びます。 「ループ(loop)」には「輪」という意味もあります。

8.3. 順次・分岐・反復の力試し(FizzBuzz)

この節では、順次・分岐・反復を使ってプログラムを1つ作ります。 プログラミングの練習によく使われる問題「FizzBuzz」(読み:フィズバズ)を解くプログラムです。

8.3.1. FizzBuzzとは

FizzBuzzは複数人で遊ぶゲームです。 1から30までの数を順番に1つずつ交互に言っていきます。 ただし、以下の条件を満たす数の場合は、数ではなく別の言葉を言う必要があります。

  • 3の倍数のとき、Fizzと言う

  • 5の倍数のとき、Buzzと言う

  • 3の倍数かつ5の倍数のとき、FizzBuzzと言う

上記3つのルールに反した人が負けになります。 6のときに「Fizz」ではなく「6」と言ってしまったり、10のときに「Buzz」と間違えて「Fizz」と言ってしまったりするとルール違反で負けです。 ルール違反なく30まで到達すると、プレイヤーは得も言われぬ達成感に包まれます。

うまくいっている例は、以下のような感じです。

  • Aさん「1」

  • Bさん「2」

  • Cさん「Fizz」

  • Dさん「4」

  • Aさん「Buzz」

  • Bさん「Fizz」

  • :

ちなみに「Fizz」は炭酸の気泡のようにシューシューいう音で、 「Buzz」はハチや機械がブンブンいう音を表します。

プログラミングの練習問題としてのFizzBuzzは、1人で遊ぶ形になります。 1人で1から30の数値を上記のルールを満たすように言って(=画面に表示して)いきます。 FizzBuzzをルール違反なく遊ぶプログラムを作って、プレイヤーの処理系を得も言われぬ達成感に包みましょう!

本書で解説したプログラムの3大構造を理解していれば、FizzBuzzは十分に解けます。 次の項から解説を始めますが、「ここまで分かれば後は自力で書けるかも」と思えたら、本を閉じて手を動かす ことをおすすめします。

以下の手順で、FizzBuzzを解くプログラム fizzbuzz.py を作ります。

  1. 1から30までを順に表示するプログラムを作る

  2. 3の倍数のとき、Fizzを表示するように変更

  3. 5の倍数のとき、Buzzを表示するように変更

  4. 3の倍数かつ5の倍数のとき、FizzBuzzを表示するように変更

8.3.2. 1から30までを順に表示する

この項目は手を動かさずに読むことをおすすめします。 新しく解説する項目があるからです。

「1から30までを順に表示」と聞いてどんな方法が思い浮かぶでしょうか? for 文と1から30までの整数が順にまとまったリストがあれば実現できそうですね。

1から30までの整数を1つずつ書いてリストを作るのは大変ですよね。 そんなときは、関数 range が使えます。 range 関数も、複数の値を扱う仕組みの1つです。

複数の整数を並べる関数

箱の名前

range

箱は何をするか

1,2,3,...のように整数を並べる

箱に入れるもの

整数2つ(1つ目が始めを表し、2つ目が終わりを表す。始めは含むが、終わりは含まない)

箱から出てくるもの

整数の並び(リストのように扱える)

for 文の練習では、リスト [1, 2, 3] から値を繰り返し表示するプログラムを書きましたね。 これは range 関数を使っても書けます。

renshu_for.py
1print("for文の練習プログラムを開始します")
2for i in range(1, 4):
3    print(i)
4print("プログラムを終了します")

range(1, 4) は、1から3までの整数の並び を返します。 これは、リスト [1, 2, 3] と同じように for 文で使えます。 range 関数の第2引数より1小さい数までという点に注意しましょう。

range 関数を使って、1から30までを順に表示しましょう (やり方に見当がついている方は本を閉じて手を動かしてみてください)。

fizzbuzz.py
1for i in range(1, 31):
2    print(i)

コマンドラインで動作確認しましょう。

> python fizzbuzz.py
1
2
3
4
5
: (省略)
28
29
30

1から30までの整数が順番に表示されました!

8.3.3. 3の倍数のとき、Fizzを表示する

この項目も手を動かさずに読むことをおすすめします(新しく解説することがあります!)。

続いて、3の倍数のとき、Fizzと表示するようにプログラムを変更します。 これは分岐を使えばいいですね。

  • 3の倍数であれば、Fizzを表示する

  • 3の倍数でなければ、その数を表示する

では、「3の倍数 である」を表す式はどうなるでしょうか。 3の倍数とは、「3で割った時に割り切れる(余りが0 になる)数」です。

プログラミング言語には、数を 割った余り を求める演算子が用意されています。 数 a を 数 b で割った余りは a % b で計算できます。

>>> 4 % 3  # ((4 ÷ 3 = 1 ・・・ 1))
1
>>> 5 % 3  # ((5 ÷ 3 = 1 ・・・ 2))
2
>>> 6 % 3  # ((6 ÷ 3 = 2 ・・・ 0))
0

3で割った余りが0かどうか比較することで、3の倍数かどうかを判定できます (やり方に見当がついている方は本を閉じて手を動かしてみてください)。

fizzbuzz.py
1for i in range(1, 31):
2    if i % 3 == 0:
3        print("Fizz")
4    else:
5        print(i)

i % 3 == 0 という式(数 i3 で割った余りが 0等しい)で 3の倍数かどうか判定します。 動作確認しましょう。

> python fizzbuzz.py
1
2
Fizz
4
5
: (省略)
28
29
Fizz

3の倍数の時にFizzと表示するようになりました!

8.3.4. 練習問題1:5の倍数のとき、Buzzを表示する

この項目はぜひ本を閉じて、手を動かしてみてください。

ヒント:5の倍数のとき、Buzzと表示するのは、3の倍数のときを参考にすればいいですね。

[解答]

fizzbuzz.py の分岐を増やします。

  • 3の倍数であれば、Fizzを表示する

  • 3の倍数ではないが5の倍数であれば、Buzzを表示する

  • 3の倍数でも5の倍数でもなければ、その数を表示する

fizzbuzz.py
1for i in range(1, 31):
2    if i % 3 == 0:
3        print("Fizz")
4    elif i % 5 == 0:
5        print("Buzz")
6    else:
7        print(i)

以上で、5の倍数のときにBuzzと表示されるようになります。

> python fizzbuzz.py
1
2
Fizz
4
Buzz
: (省略)
28
29
Fizz

最後の 30 は3の倍数であり、5の倍数でもあります。 分岐では、まず3の倍数か確認 し、30 は3の倍数と判定されるので、Fizzと表示されていますね。 次はいよいよ、ここをFizzBuzzという表示に変えます!

8.3.5. 練習問題2:3の倍数かつ5の倍数のとき、FizzBuzzを表示する

この項目もこれまでに学んだことで進められます。 本を閉じて、手を動かしてみてください。

ヒント:「3の倍数かつ5の倍数」とは、「15の倍数」と同じです。 15の倍数と考えると、3の倍数や5の倍数のときと同じようにプログラムを書けそうですよね。

[解答] elif で分岐を追加すると?

15の倍数のときの分岐を追加してみます(6, 7行目)。

fizzbuzz.py(15の倍数の分岐を追加)
1for i in range(1, 31):
2    if i % 3 == 0:
3        print("Fizz")
4    elif i % 5 == 0:
5        print("Buzz")
6    elif i % 15 == 0:
7        print("FizzBuzz")
8    else:
9        print(i)

「できた!」と思って動作確認をすると・・・

> python fizzbuzz.py
1
2
Fizz
4
Buzz
: (省略)
28
29
Fizz

最後の数字 30 は15の倍数なので、FizzBuzzと表示するルールです。 ところが、現在のプログラムはFizzと表示していて、ルールに違反しています(負けてしまいました)。

なぜこのような動きになっているか理解し、ルールを守るようにプログラムを直しましょう。

現在の分岐(fizzbuzz.py 2〜9行目)は以下のようになっています。

  • 3の倍数であれば、Fizzを表示する

  • 3の倍数ではないが5の倍数であれば、Buzzを表示する

  • 3の倍数でも5の倍数でもないが15の倍数であれば、FizzBuzzを表示する

  • 3の倍数でも5の倍数でも15の倍数でもなければ、その数を表示する

この分岐が、30のような15の倍数をどのように処理するか考えましょう。 30は3の倍数 なので、fizzbuzz.py の2,3行目によりFizzを表示しますね。 FizzBuzzを表示する分岐が実行されることはないのです。

修正案が浮かんでいたら、続きを読む前に試してみてください。

[解答]

15の倍数の判定を一番最初に(if の直後の式で)実施するように分岐を修正します。

15の倍数は3の倍数でも5の倍数でもある ので、15の倍数かどうかは最初に判定する必要があるのです。 逆に15の倍数でないとき、その数は3の倍数または5の倍数のいずれかかもしれません。 これらを判定する分岐は、elif で追加すれば十分です。

fizzbuzz.py(正解例)
1for i in range(1, 31):
2    if i % 15 == 0:
3        print("FizzBuzz")
4    elif i % 3 == 0:  # ((ifからelifに変更))
5        print("Fizz")
6    elif i % 5 == 0:
7        print("Buzz")
8    else:
9        print(i)

動作確認しましょう。

> python fizzbuzz.py
1
2
Fizz
4
Buzz
: (省略)
28
29
FizzBuzz

30の行の表示がFizzBuzzになっています! また、手元で15の行の表示も確認してみてください。

以上でFizzBuzzを解くプログラムは完成です。

最後に紹介したプログラムが一発で書けるのを目標に何回か取り組んでみてください。

実はFizzBuzzはプログラムが書けない面接候補者をふるい落とすために15年前くらいから使われ始めたという出自があります。 FizzBuzzが独力で解けるようになったら、「プログラマーとしての基礎の力は身に付いた!」と自信を持ってください。

[コラム] あとは順次・分岐・反復を使う練習あるのみ!

順次・分岐・反復を使ってFizzBuzzのプログラムを書きました。 どんなプログラムでも、それを形作る 構造 は、FizzBuzzのプログラムと共通の 順次・分岐・反復 です。 ここまで読み進めてきた皆さん、あとはプログラムを書く練習あるのみです!

続く2つの章は、この本の残りを読み進める上で必要になる事項の紹介です。 ですが、すぐにでも練習したいという方は、実際に使えるプログラムを作る他書に進み、 必要になったタイミングで戻ってくるという読み方でもかまいません。