roy > naoya > 基礎プログラミングI·情報検索 > (4)制御構造[2]

(4) 05/11の授業内容:制御構造[2]

[1] 繰り返しの有効性

プログラムの流れを変える働きをするものを制御構造とよび、前回の授業では条件判断を取り上げた。制御構造にはもう1つ繰り返しがある。繰り返しについて考えるにあたり、まず次のプログラムを作ることを考えてみる。

バーコードリーダーを使ってバーコードを読み取り、商品名と金額を取得する。レジの「合計」ボタンを押すと合計金額が計算され、表示される。

お客さんが購入する商品の個数は一定ではないため、合計を出すために必要な足し算の回数は毎回変化する。プログラムを書く際は、合計金額を最初0円としておき、バーコードリーダーを介して取得される金額を随時合計金額に加算していくことになる。

このプログラムの流れをフローチャートを用いて図式的に表現してみよう。左側がプログラムを上から順番に実行していく方法、右側が今回登場する繰り返しを用いた表現方法である。

繰り返しをしないと同じことを何度もプログラムに書かなければならない

左側から見ると、まず合計金額の初期値を0円としている。次にバーコードを読み取って金額を取得し、その金額を合計金額に加算する。これで1つ目の商品の金額が合計に加算されたことになる。次に2つ目の商品のバーコードを読取り、合計金額に加算する。「バーコードを読み取る」「合計金額に加算する」という処理が繰り返し登場していることがわかる。ここでは2回分しか書いていないが、実際には3個以上の商品を購入する場合もありうるのでこれでは足りない。最大で何回分準備しておけばよいのかは難しい判断だが、ここでは念のため100個分準備しておけば安心と考え、100回同じことを書いている。

右側は繰り返しを用いたプログラムの考え方である。ここでは「バーコードを読み取る」「合計金額に加算する」の後で◇があり、ここで合計ボタンを押したかどうか判定が行われる。押した場合はお会計に進み、押していない場合は上に戻り、次の商品の金額を合計に加算する。このような書き方をすると、バーコードからの金額の取得と合計への加算をプログラム内で1回書いておけばよいことになり、シンプルになる。

制御構造における繰り返し

  • 同じ処理を繰り返し行う場合に、繰り返し表現を用いると1回書けばよい。

[2] 繰り返し(1):while-end

繰り返しを表現する際に主に用いられるのはwhile-end(while文)である。以下がwhile文の基本構造となる。

while 繰り返しを継続する条件
  繰り返し行う処理
end

whileの横に繰り返しを継続する条件を記載する。その条件を満たす間はwhileとendの間に書かれた行を繰り返し実行する。最後には必ずendをつける。

簡単なプログラムを見てみよう(add.rb)。

#!/usr/koeki/bin/ruby

sum = 0
i = 1
while i <= 5
   sum += i
   i += 1
end
printf("1から5の合計は%dです。\n",sum)

これは1から5まで足した結果を求めるプログラムであり、実行すると以下の結果が得られる。

pan{c10xxxx}% ruby add.rb[Return]
1から5の合計は15です。

このプログラムでは冒頭で、sumに0、iに1を代入している。その後while-endが登場するが、whileの横に書かれた繰り返しを継続する条件は「i <= 5」、whileとendの間にある繰り返し行う処理は「sum += i」「i += 1」の2つとなっている。では、このプログラムでどのように1から5までの足し算をしているのだろうか。

以下は、add.rbを説明するにあたり左端に行番号を付加したものである。この行番号に基づいてプログラムの流れを示す。

#!/usr/koeki/bin/ruby

1:sum = 0
2:i = 1
3:while i <= 5 #=> iの値が5以下の間は以下の2行を繰り返し実施する
4:   sum += i
5:   i += 1
6:end
7:printf("1から5の合計は%dです。\n",sum)
  1. 1行目:sumに0が代入される。
  2. 2行目:iに1が代入される
  3. 3行目:iは1なので5以下である。このため以下の2行を実施する。
  4. 4行目:sumiを加算代入する。sumは1となる。
  5. 5行目:iに1を加算代入する。iは2となる。
  6. 6行目:endに到達したのでとりあえず3行目に戻る。
  7. 3行目:iは2なので5以下である。このため以下の2行を実施する。
  8. 4行目:sumiを加算代入する。sumは3となる。
  9. 5行目:iに1を加算代入する。iは3となる。
  10. 6行目:endに到達したのでとりあえず3行目に戻る。
  11. 3行目:iは3なので5以下である。このため以下の2行を実施する。
  12. 4行目:sumiを加算代入する。sumは6となる。
  13. 5行目:iに1を加算代入する。iは4となる。
  14. 6行目:endに到達したのでとりあえず3行目に戻る。
  15. 3行目:iは4なので5以下である。このため以下の2行を実施する。
  16. 4行目:sumiを加算代入する。sumは10となる。
  17. 5行目:iに1を加算代入する。iは5となる。
  18. 6行目:endに到達したのでとりあえず3行目に戻る。
  19. 3行目:iは5なので5以下である。このため以下の2行を実施する。
  20. 4行目:sumiを加算代入する。sumは15となる。
  21. 5行目:iに1を加算代入する。iは6となる。
  22. 6行目:endに到達したのでとりあえず3行目に戻る。
  23. 3行目:iは6なので5以下という条件を満たさない。このため繰り返しを終了する。
  24. 7行目:「1から5の合計は15です。」と表示される。

繰り返しを行う中で、iの値を1ずつ増加させている(5行目)。そしてiの値に基づいて繰り返し継続の可否を決定している(3行目)。

もう1つ例を見てみよう。今度は6回のテストの合計得点を求めるプログラムである(test.rb)。

#!/usr/koeki/bin/ruby

total = 0
number = 1

print"6回のテストの合計得点を求めます。\n"

while number <= 6
   printf("%d回目のテストの得点は?\n", number)
   score = gets.chomp!.to_i
   total += score
   number += 1
end
printf("6回のテストの合計は%dです。\n",total)

これを実行すると次の結果が得られる。

pan{c10xxxx}% ruby test.rb[Return]
6回のテストの合計得点を求めます。
1回目のテストの得点は?
30[Return]
2回目のテストの得点は?
45[Return]
3回目のテストの得点は?
25[Return]
4回目のテストの得点は?
30[Return]
5回目のテストの得点は?
50[Return]
6回目のテストの得点は?
40[Return]
6回のテストの合計は220点です。

ここではwhile-end内でnumberの値を1ずつ増加させ、whileの横に書かれた条件であるnumber <= 6を満たす間繰り返し処理を行わせている。

while-end間にはprintfメソッドやgetsメソッドが用いられており、繰り返しを行うたびに、メッセージの出力やキーボードからの入力の受け取りが行われている。

while文のまとめ

  • 次の基本構造を持つ。
while 繰り返しを継続する条件
  繰り返し行う処理
end
  • 最後にはendを必ずつける。

[3] 繰り返し(2):for-end

for文は様々な使い方ができ、もう少し後の回(具体的には配列を学んだ後)に役に立つ表現方法である。ここでは最も簡単なfor文の使い方を示す。

for 変数 in 開始時の数値..終了時の数値 do
   繰り返したい処理
end

これがfor文の基本構造である。forの横に記載した変数にin以下の値を順次代入しながらその下に書かれた処理を繰り返し実行する。for-endを用いた足し算プログラムの例を示す。

#!/usr/koeki/bin/ruby

sum = 0
for i in 1..5
   sum += i
end
printf("合計は%dです。\n",sum)

上記のプログラムでは、iに1から5まで1ずつ増やしながら順番に代入していき、その下にあるsum += iという処理を繰り返し実行することになる。iの値は1、2、3、4、5と変化していくため、これに合わせてsumの値は1、3、6、10、15と変化する。これは結果的に1から5までの足し算をしていることになるのでwhile文で書くこともできる。

[4] 繰り返し(3):その他の繰り返し[補足]

>>More

[5] 出席課題

add.rbを参考に1から10までの積(1×2×・・・×10)を求めるプログラムを書いてみよう。最初から書くのは大変なのでadd.rbを修正してみよう(multiple.rb)。while文で書けば良いが、for文に挑戦しても良い。

制限時間は10分。うまくいかない場合は、できたところまででよいのでメールで解答を送信すること。出席点は2点。提出要領は下記の通り。

  • 提出先:課題提出用メールアドレス
  • メールのSubject:ruby04
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降にプログラムおよび実行結果を貼りつける。その他説明を加えても良い。

[6] while-endの特殊な用法(while true)

本日の冒頭で話したバーコードを読み取るレジのプログラムを思い出そう。ここでは複数商品の会計をすることができるように、以下の部分を繰り返して実行していた。

  • バーコードを読取り金額を取得する。
  • その金額を合計金額に加算する。
  • 合計ボタンを押したらお会計、押さなければ次の商品のバーコードを読み取る

これをwhile-endを使って書くことを考えよう。これまではwhile-end内で特定の変数の値を一定の値ずつ増加させ、その変数の値があらかじめ指定した値を超えた時点で繰り返しが終了するようにしていた。

しかし、今回はこのような方略を採用することはできない。合計ボタンを押したときに繰り返しを終了しなければならないからである。つまりユーザーからの入力によって繰り返しを終了する必要があり、あらかじめ繰り返しの回数を定めておくことができない。

このような場合にはwhile trueを用いる。trueとは条件を満たすという意味である。したがってwhile trueは、whileの横の繰り返し継続条件が「常に条件を満たす」ということになり、永久に繰り返しを継続せよという構文になる。このままでは本当に永久にプログラムが継続するため、while-end内に別途繰り返し終了条件を設ける。まずはwhile trueを用いたプログラム(register.rb)を確認してみよう。

#!/usr/koeki/bin/ruby

sum = 0

while true
  print"金額を入力してください(お会計は0と入力):\n "
  price = gets.chomp!.to_i
  if price == 0 then
    break
  end
  sum += price
end

printf("ありがとうございます。お会計 %d 円になります。\n", sum)
pan{c10xxxx}% ruby register.rb[Return]
金額を入力してください(お会計は0と入力):
200[Return]
金額を入力してください(お会計は0と入力):
100[Return]
金額を入力してください(お会計は0と入力):
350[Return]
金額を入力してください(お会計は0と入力):
0[Return]
ありがとうございます。お会計650円になります。

while-end内では、printメソッドでメッセージを表示し、getsメソッドを使ってユーザーからの金額の入力を受け取りpriceに代入している。そして、priceの値をsumに加算代入することで、繰り返しを行うたびにsumの値は増加する。

新しく登場したのはプログラム中に黄色で示した、while trueとif-endである。

  • while true:trueは条件を満たすという意味。while tureとすると常に条件を満たすことになり、永久に繰り返しが行われる。このままでは本当に終了しないので、while-end内に別途繰り返しを終了する条件を入れる。具体的にはif文を使用し、if文の中にbreakを入れる。
  • break:繰り返しから抜ける命令。このプログラムではpriceに代入された値が0であれば(=ユーザが0を入力したら)、breakが実行される。breakが実行されるとwhile-endの繰り返しから抜け出し、endの下のprintfの行に進む(for-endやuntil-endでも使用することができる)。

while trueについてのまとめ

  • ユーザの入力に基づいて繰り返しを終了する場合などにはwhileの横に繰り返しを継続する条件を書くかわりにwhile tureとする。
  • while trueとすると永久に繰り返しが継続し、終了しない。
  • while-end内にif文を設け、if文の中にbreakを入れる。breakは繰り返しから抜ける命令で、if文に記載された条件を満たす場合に繰り返しを終了することになる。

[7] break、redo、next

breakと同じように、while-end、for-end、until-endで使用することができる繰り返し制御の記法を参考までに以下に示す。

  • break:繰り返しの実行を中止し、繰り返しから抜ける
  • redo:その回の繰り返しを無効化し、その回の繰り返しの先頭からやりなおす
  • next:その回の繰り返しを無効化し、次の繰り返しに突入する

redoについては以下のように不適切な入力に対する再入力の促しとして使用できる。


while true
  print"1、2、3のいずれかを入力:\n "
  number = gets.chomp!.to_i
  if number < 1 || number > 3 then
    print"1、2、3のいずれかを入力してください\n"
    redo
  end
  (略)
end

printf("ありがとうございます。お会計 %d 円になります。\n", sum)

if number < 1 || number > 3 thenでは、「||」を使って2つの条件を併記している。「||」は「または」という意味であるため、この条件はnumberが1未満または3を超える場合ということになる。

この条件を満たす場合には、次のprintの行が実行され正しい値の入力が促される。そして、redoを行うことで、直前に入力した値を無効にして、while trueの真下の行に戻される。

[8] インデントをして見やすくしよう

register.rbを見ると、なぜか字下げ(インデント)されている箇所がある。while-end内やif-end内がインデントされている。インデントをすることでwhile文やif文がどこで始まりどこで終わっているかわかりやすくなる。以下には同一のプログラムを(1)インデントをした場合、(2)インデントをしない場合の2パターンで示す。(1)インデントをした場合の方が見やすいのは明らかである。

#!/usr/koeki/bin/ruby

sum = 0

while true
  print"金額を入力してください(お会計は0と入力):\n "
  price = gets.chomp!.to_i
  if price == 0 then
    break
  end
  sum += price
end

printf("ありがとうございます。お会計 %d 円になります。\n", sum)
#!/usr/koeki/bin/ruby

sum = 0

while true
print"金額を入力してください(お会計は0と入力):\n "
price = gets.chomp!.to_i
if price == 0
then break
end
sum += price
end

printf("ありがとうございます。お会計 %d 円になります。\n", sum)

もう少し長いプログラムを書くようになると、while文の中にwhile文が登場したり、さらにif文が登場したりして、endを複数使用するようになる。endがないとプログラムはうまく動かない。インデントをする習慣をつけるとendの抜け落ちに気づきやすくなる。

インデントを行う場合、スペースキーを何回か押しても良いが、Tabキーを押すと自動的に適切な位置にインデントをしてくれる。便利なので確認しておこう。

プログラムのインデントについて

  • インデントをすると見やすさが向上し、誤りも軽減できる。
  • スペースキーを複数回押すのではなく、Tabキーを押すと自動的に適切な位置にインデントしてくれる。

[9] レポート課題

register.rbを改良し、この授業の成績をつける以下のプログラムを作成せよ(report2.rb)(8点満点)。

出席回数と8回分のレポートの得点を入力すると、最後に合計得点と成績が表示されるプログラム。なお、出席は1回あたり2点、レポートはそれぞれ8点満点とする。成績は、60点未満「不可」、70点未満「可」、80点未満「良」、80点以上「優」とする。

作成上の条件

  • while-endを使用すること
  • whileの横に具体的に繰り返し継続条件を記載しても良いし、while trueを用いても良い
  • whileの横に繰り返し継続条件を記載する場合、繰り返し回数は8回とすること
  • while trueを用いる場合は何回でも繰り返せるが、実行時は8回入力すること
  • printやprintfメソッドを用いて、初めてそのプログラムを実行したユーザでも使い方が分かるような文章を提示するよう心がけること
  • 最後の成績表示部は合計得点によってメッセージが異なる。if-endを用いて記載すること
  • 成績は単に「不可」や「優」と表示するだけでなく、「評価は優です。頑張りましたね。」というように趣向を凝らしたメッセージにしても良い

実行結果のイメージは以下の通り(黄色がユーザの入力)。

pan{c10xxxx}% ruby report2.rb[Return]
出席回数を入力してください
14[Return]
1回目のレポートの得点を入力してください
7[Return]
2回目のレポートの得点を入力してください
6[Return]
      :
合計得点は82点です。
評価は「優」です。おめでとうございます!

提出要領

  • 提出先:課題提出用メールアドレス
  • 提出期限:5/17(日)23:00
  • メールのSubject:report02
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は下記の構成とする
    1. 作成したプログラム
    2. プログラムの実行結果
    3. プログラムの説明
    4. 感想や気づいた点

採点要領

  • 採点基準:期限内提出点(2点)、メールの体裁(1点)、プログラム(2点)、プログラムの説明(2点)、メッセージの工夫(1点)
  • プログラムの説明は、今日新しく出てきたwhile文を中心に行えば良いが、何が重要か判断できない場合は1行ずつ説明してもよい。
  • メッセージの工夫は、while-end内やif-end内のprint、printfのメッセージのわかりやすさで評価する。
  • わかりにくい説明や、Webページを単にコピー&ペーストしただけの説明は減点することがある。一度読み直してから提出すること。
  • 驚異的に良くできているレポートについては満点の8点を超える得点をつけることがある。
  • よくできていたレポートは、他の人の参考になるよう、本人が特定できないような形で掲載する。掲載してほしくない場合はメールでの課題提出時にその旨記載すること。