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

(4) 制御構造[2]

[1] 条件判断への導入

前回は、制御構造のうち「繰り返し」について学んだ。今回はもう一つの制御構造である「条件判断」について確認した上で、「繰り返し」表現の記法であるwhile-endと組み合わせた用法について確認する。

条件判断は次のような場面で活用できる。

  • 計算問題を出す。正解していれば「正解です!」と表示し、間違っていれば「不正解です!」と表示する。
  • 5問クイズを出す。4問以上正解で「ワンダフル!」、それ以外は「まあまあ」と表示する。

これらの例を図式的に(フローチャートで)表現すると以下のようになる。◇の箇所で条件を満たしているかどうか判定が行われ、満たしている場合と満たしていない場合では異なる動作を行っている(前回と同じ図)。

上記の例はいずれも分岐が必要になる(フローチャートで表現)

プログラムでこれを表現する場合、左右に分けて書くことができないので、上下に分割して書く。

正解している場合
  正解です!と表示
そうでない場合
  不正解です!と表示

プログラムは上から進んでくるが、この部分では条件に応じて「正解です!」が表示されたり「不正解です!」が表示されたりする。順番に1行ずつ実行されて両方のメッセージが表示されることはない。

[2] 条件判断(1):if-end

条件判断の代表格となるのがif-endである。if文と呼ぶこともあるが同じものである。

条件が1つの場合

if文の基本構造を先ほどの日本語で書いた条件分岐の例と対比させながら確認してみよう

正解している場合
  正解です!と表示
そうでない場合
  不正解です!と表示
if 条件A    #=>条件Aを満たせば
  処理A    #=>処理Aを実施する
else       #=>条件Aを満たさない場合は
  処理B    #=>処理Bを実施する
end

「正解している場合」に対応するのが「if 条件A」となる。ifの横に具体的に条件を書く。「そうでない場合」は「else」と表現する。最後にはendがついている。endをつけ忘れるとプログラムが動かない。この構造を把握するために、以下のプログラムをif.rbという名称で保存し、実行してみよう。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

print"問題です。\n"
print"28の約数の和は28になる。\n"
print"Yesの場合は1を、Noの場合は2を入力\n"
answer = gets.chomp!.to_i

if answer == 1
  print"正解です!\n"
else
  print"不正解です!\n"
end

ifの横には「answer == 1」という条件が書かれている。==右辺と左辺が等しいという論理演算子である。つまり条件は「answer(に代入されている値)が1である」ということになる。このプログラムの実行結果を以下に示す。正解した場合と不正解の場合いずれも示すので、各自で実行した際に同じ結果となるか確認してみよう。

sime{c11xxxx}% ruby if.rb[Return]
問題です。
28の約数の和は28になる。
Yesの場合は1を、Noの場合は2を入力
1[Return]
正解です!
sime{c11xxxx}% ruby if.rb[Return]
問題です。
28の約数の和は28になる。
Yesの場合は1を、Noの場合は2を入力
2[Return]
不正解です!

条件が2つ以上の場合

次はもう少し複雑なケースを考えてみよう。以下は80点以上ならA、60点以上ならB、60点未満ならCと表示するという状況をフローチャートで表現したものである。1つ目の◇で80点以上という条件を満たしているか判定が行われ、満たさない場合は2回目の◇で60点以上という条件を満たしているかどうか調べている。

条件が2つある場合は2回判断をする必要がある(フローチャートで表現)

このように2つの条件がある場合、if文ではどのように書くのだろうか。このフローチャートをプログラムで表現したものが以下(hantei.rb)である。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

print"得点を入力してください\n"
score = gets.chomp!.to_i

if score >= 80     #=>1つ目の条件を記載
  print"A評価です\n"  #=>1つ目の条件を満たす場合に実施
elsif score >= 60  #=>1つ目の条件を満たさない場合2つ目の条件を満たすか判定
  print"B評価です\n"  #=>2つ目の条件を満たす場合に実施
else                 #=>いずれも満たさない場合は
  print"C評価です\n"  #=>これを実施する
end

2つ目の条件を記載するためにelsifが登場している。ifの横に記載された1つ目の条件を満たすか否かがまず判定され、満たした場合にはその下に書かれた処理が実行される。満たさない場合にはelsifの行で2つ目の条件を満たすかどうか判定が行われ、満たす場合はその下の処理、満たさない場合はelse以下の処理が行われる。最後には必ずendがつく。

なお、ifの行やelsifの行で登場している「>=」は≧と同じ意味である。キーボードには≧のキーはないので、≧のかわりに「>=」としている。

条件が3つ以上になった場合はelsifを何度も使用して記述する。これについて詳細は[3]if-endの応用[補足]を参照のこと。

if文のまとめ

  • 次の基本構造を持つ。
if 条件A
  処理A
elsif 条件B
  処理B
else
  処理C
end
  • 最後にはendを必ずつける。
  • 条件が3つ以上の場合はelsifを繰り返し使用する。

[3] 条件判断(1'):if-endの応用[補足]

>>More

[4] 条件判断(2):その他の条件判断[補足]

>>More

[5] 出席課題

hantei.rbを書き換え、S~Dの5段階評価ができるようにしてみよう(hantei2.rb)。得点と評価の関係は以下の通り。

  • 90点以上:S
  • 80点以上:A
  • 70点以上:B
  • 60点以上:C
  • 60点未満:D

hantei.rbを別名で保存してから修正を行うと良い。Ctrl+x Ctrl+wで名前をつけて保存になる。programディレクトリにhantei2.rbという名称で保存をしてから修正を始めよう。

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

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

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

while true

コンビニやスーパーのレジのプログラムを書くことを考えてみよう。複数の商品を購入することを考えると、以下の部分を繰り返して実行する、すなわちwhile-endを用いて書くことが予想される。

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

では、while-endを使ってどのように書いたらよいだろうか。前回はwhileの横に繰り返し継続条件を書き、その条件を満たさなくなった時点で繰り返しから抜けるようにしていた。

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

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

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0

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

printf("ありがとうございます。お会計 %d 円になります。\n", sum)
sime{c11xxxx}% 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文に記載された条件を満たす場合に繰り返しを終了することになる。

breakするための条件は多様

register.rbではユーザが0と入力することで繰り返しから抜けたが、breakするための条件は自由に設定することができる。

まずは、ユーザがキーボードからquitという文字列を入力したときにbreakするように書き換えてみよう(register2.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0

while true
  print"金額を入力(終了はquit):\n"
  price = gets.chomp!
  if price == "quit"
    break
  end
  sum += price.to_i
end

printf("合計%d円です。\n", sum)

このプログラムでは、ユーザがキーボードから入力する値は2種類の型がある。

  • 整数:商品の金額
  • 文字列:breakするための「quit」

register.rbでは、price = gets.chomp!.to_iというようにgetsメソッドの後ろに、to_iメソッドをつけていた。to_iメソッドは値の型を整数に変換するメソッドである。ユーザが商品の金額を入力した場合は、整数変換した上でpriceに値が代入されるので問題はないが、quitを入力した場合はどうなるだろうか。

quitなどの文字列は整数に変換すると0になる。このため、キーボードからquitを入力してもpriceに代入される際には0になってしまう。したがって、price = gets.chomp!.to_iとするとpriceに代入された値がquitである場合にbreakするというif文に記載された条件を満たすことができない。

正しく終了するためにはto_iの位置を変更する必要がある。gets.chomp!.to_iをgets.chomp!に修正し、to_iをsum += price.to_iの位置につけ、sumに金額を加算する際に整数に変換すればうまく動くようになる。

なお、breakするための条件を書き間違え、繰り返しから抜けられなくなった場合は、Ctrl+cで強制終了することができる。

sime{c11xxxx}% ruby regi.rb[Return]
金額を入力(終了はquit):
198[Return]
金額を入力(終了はquit):
298[Return]
金額を入力(終了はquit):
quit[Return]
合計496円です。

値の型変換メソッドをつける位置について

  • 変数.to_iのように変数につけることができる。
  • アルファベットや日本語を整数に変換すると0になる。
  • プログラムが止まらなくなったらCtrl+cで強制終了できる。

[7] break、redo、next

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

  • break:繰り返しの実行を中止し、繰り返しから抜ける
  • redo:その回の繰り返しを無効化し、その回の繰り返しの先頭からやりなおす(whileの横の繰り返し継続条件は満たしているものとして扱われる)
  • next:その回の繰り返しを無効化し、次の繰り返しに突入する(次の繰り返しを実施するかどうかはwhileの横の条件次第)

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


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

(略)

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

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

[8] テスト

プログラムが完成したら、間違いがないか確認しよう。構文の間違いなどがあれば実行時にエラーとなるが、if文やwhile文の条件に間違いがある場合は実行できてしまう。間違いがあることに気づかないまま使ってしまえば、望んだ結果を得ることができなくなる。そこで行うのがテストということになる。テストはプログラムに間違いがないことを確認するために行う工程である。

ブラックボックステスト

プログラムの内部構造を考慮せずに行うテスト。例えば、「1~10の数字を入力してください」というメッセージに対して、正しい値として1や10、不適切な値として0や11を入力値として与え、正しい値の場合に正しい結果が、不適切な値の場合には不適切な値であることを促すメッセージが表示されるかどうかを確認する。

ホワイトボックステスト

プログラムの内部構造を踏まえた上で、すべての条件分岐を試していずれもエラーが出ないことを確認するテスト。例えば、if文でif側だけを試すのではなく、elsifの場合やelseの場合も実行していずれも適切な結果が得られることを確認する。

[9] レポート課題

課題

前回作成したreport2a.rbについて以下の改良をできるところまで実施しなさい。必ず1番から順に実施すること(report3.rb)(10点満点)

  1. 最後に合計得点だけでなく、平均点が表示できるようにする(小数点以下は第1位まで)
  2. while trueを用いて書き直し、キーボードからexitと入力した際に繰り返しから抜ける(breakする)ようにする(この場合でも平均値が正しくなるようにすること)
  3. breakするための条件にレポートの得点を8回入力した場合も追加する(条件が2つになる)
  4. レポートの得点は最高8点とし、入力した得点がマイナスの場合や8点を超える場合は入力間違いとみなして、再入力をうながすようにする
  5. 極めてよくできたレポートの場合は8点を超える点をつける場合があるので、8点を超える得点を入力した場合には、正しい得点かどうか確認し、正しければ合計に加算し、誤っていれば再入力をうながす(マイナスの場合は無条件で再入力)

提出要領

  • 提出先:課題提出先メールアドレス
  • 提出期限:第1提出期限、第2提出期限を設定
  • メールのSubject:report03
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は下記の構成とする
    1. 何番まで実施したか
    2. 作成したプログラム
    3. プログラムの実行結果
    4. プログラムの説明
    5. 感想
    6. 参考文献
    7. 添付ファイル(report3.rb)

作成要領

  • 採点基準:期限内提出点(2点)、メールの体裁(1点)、プログラム(5点)、プログラムの説明(2点)
  • 実行結果は1回分だけでなく、ブラックボックステストやホワイトボックステストを行い、間違いがないことを確認する。
  • プログラムの説明は、それぞれの修正について、変更点と変更理由がわかるように説明する。
  • プログラムについては、5個の修正につき1つ1点で評価する。
  • 他人のレポートを丸写しした場合は、写した側、写させた側共に0点とする。
  • わかりにくい説明や、Webページを単にコピー&ペーストしただけの説明は減点する。一度読み直してから提出すること。
  • 驚異的に良くできているレポートについては満点の8点を超える得点をつけることがある。
  • よくできていたレポートは、他の人の参考になるよう、本人が特定できないような形で掲載する。掲載してほしくない場合はメールでの課題提出時にその旨記載すること。