プログラムは通常、1行目から順番に実行されて、終了する。この流れを変えるのが制御構造である。制御構造によって、プログラムの一部を実行させなかったり、同じ処理が何度も繰り返されるようにさせたりすることができる。以下はよく使う制御構造である条件分岐と繰り返し処理について説明する。
詳細についてはRubyリファレンスマニュアルを参照してください。
文法:
if 条件式 then 処理 (elsif 条件式 then 処理 ) (...) (else 処理 ) end
条件式の結果が真である場合、then以下の処理を行う。ifの条件式が偽の場合、elsifがあればelsifの条件式を評価する(elsifは「else if」の省略である)。elsifは複数指定できる。ifも、全てのelsifの条件式が偽であった場合、elseがあればその以下の処理を行う。thenは省略可能である。
ある条件が成り立つかどうかを表す値を真偽値という。Rubyではfalseと「値がない状態」を表す特殊値であるnilだけが偽で、それ以外は0や空文字列を含めて全てのオブジェクトが真とみなされる。
if 10 > 9 then # 条件が成り立つ(真)
...
end
if 10 < 9 then # 条件が成り立たない(偽)
...
end
if 10 # 条件が成り立つ(真)
...
end
if nil then # 条件が成り立たない(偽)
...
end
if文を使うプログラムquiz.rbを作成せよ。
練習問題 quiz.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
puts "Rubyはインタプリタ型のプログラミング言語ですか?(y/n)"
answer = gets.chomp
if answer == 'y' then
puts "正解です!"
elsif answer == 'n' then
puts "違います!"
else
puts "「y」か「n」と答えてください。"
end
実行結果の例(黄色い部分はユーザ入力である):
Rubyはインタプリタ型のプログラミング言語ですか?(y/n)
y
正解です!
解説:
同じ条件分岐で複数の条件を組み合わせることができる。ユーザーが使うと想定できる様々な回答を扱うようにquiz.rbを改良しよう。
練習問題 quiz2.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
puts "Rubyはインタプリタ型のプログラミング言語ですか?(y/n)"
answer = gets.chomp
if answer == 'y' or answer == 'yes' or answer == 'はい'
puts "正解です!"
elsif answer == 'n' or answer == 'no' or answer == 'いいえ'
puts "違います!"
else
puts "「y/yes/はい」または「n/no/いいえ」と答えてください。"
end
実行結果の例(黄色い部分はユーザ入力である):
Rubyはインタプリタ型のプログラミング言語ですか?(y/n)
はい
正解です!
解説:このように or で区切って複数の条件式を記述するとどちらか一つの条件だけが満たされていたら該当する処理が実行される。or の代わりに and を使えば全ての条件が同時に成り立たなければならないことになる。
一つのオブジェクトの値によって違う処理を行いたいときはcase文を使うことが多い。
文法:
case オブジェクト when 値1 処理1 when 値2 処理2 when 値3 処理3 (...) (else その他の処理 ) end
「when 値 処理」はいくつも追加できる。オブジェクトの値がwhenブロックで与えられた値のいずれとも一致していない場合、elseがあればその以下の処理を行う。
case文を使うプログラムgreetings.rbを作成せよ。
練習問題 greetings.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
puts "どの言語であいさつしましょうか?"
puts "Options:"
puts "日本語"
puts "ポーランド語"
puts "アイヌ語"
lang = gets.chomp
case lang
when "日本語"
puts "こんにちは"
when "ポーランド語"
puts "Dzien dobry"
when "アイヌ語"
puts "Irankarapte"
else
puts "Hello"
end
実行結果の例(黄色い部分はユーザ入力である):
どの言語であいさつしましょうか?
Options:
日本語
ポーランド語
アイヌ語
ポーランド語
Dzien dobry
ある条件のもとで同じ処理を繰り返す制御構造をループ(loop)という。以下はRubyでループを実行するための方法であるwhile文、for文、そしてtimesメソッドについて説明する。
文法:
while 条件式 do 処理 end
条件式が真である間、処理を繰り返し実行する。 doは省略可能である。
while文を使うプログラムを作成せよ。
練習問題 while_loop.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
number = 1
while number <= 10
printf("%d番目の繰り返しです\n", number)
number += 1
end
結果:
1番目の繰り返しです
2番目の繰り返しです
3番目の繰り返しです
4番目の繰り返しです
5番目の繰り返しです
6番目の繰り返しです
7番目の繰り返しです
8番目の繰り返しです
9番目の繰り返しです
10番目の繰り返しです
解説:printfを使って number の現在の値を出力し「number += 1」で1つ増やす処理を繰り返すだけのプログラムである。number が 11 になると「number <= 10」という条件が成立しなくなるのでループが修了する。
文法:
for 変数 in オブジェクト do 処理 end
配列や範囲オブジェクトなど、複数の要素を持つオブジェクトの各要素を順番に変数に代入して処理を繰り返す。doは省略可能である。
for文による配列処理の使用例は配列について学ぶ講義のときに登場する。ここでは1から10までの範囲を使った例を示す。
練習問題 for_loop.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
for number in 1..10
printf("%d番目の繰り返しです\n", number)
end
結果:
1番目の繰り返しです
2番目の繰り返しです
3番目の繰り返しです
4番目の繰り返しです
5番目の繰り返しです
6番目の繰り返しです
7番目の繰り返しです
8番目の繰り返しです
9番目の繰り返しです
10番目の繰り返しです
解説:「1..10」は 1 から 10 までの整数を表すRangeクラスのオブジェクトである。「for number in 1..10」と書くことで、このオブジェクトの各要素を順番に取り出して、numberを通じてループ内の処理で参照できるようになる(この例では今回の値を表示するだけの処理である)。
文法:
回数.times do |変数| 処理 end
決まった回数だけ処理を繰り返す。doの後(「|変数|」のように縦棒で囲んで)変数を指定すると、その変数を通じて処理の中で現在の繰り返しの回数を参照できる(必要ない場合は省略してて良い)。doは省略できない。
繰り返し処理の回数は0から始まることに注意してください。
練習問題 times_loop.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
10.times do |number|
printf("%d番目の繰り返しです\n", number)
end
結果:
0番目の繰り返しです
1番目の繰り返しです
2番目の繰り返しです
3番目の繰り返しです
4番目の繰り返しです
5番目の繰り返しです
6番目の繰り返しです
7番目の繰り返しです
8番目の繰り返しです
9番目の繰り返しです
while文などの繰り返し処理において、途中で繰り返し処理を中断したり、 次の回に飛ばしたり、再度やり直したりしたい場合がある。そのような時は、繰り返し処理の中でbreak、next、redoを用いる。
breakは、繰り返し処理を中断してループを終了させる。
練習問題 loop_break.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
number = 0
max = 10
while number < max
number += 1
if number % 5 == 0
break
end
printf("%d回のうち、%d番目の繰り返しです\n", max, number)
end
結果:
10回のうち、1番目の繰り返しです
10回のうち、2番目の繰り返しです
10回のうち、3番目の繰り返しです
10回のうち、4番目の繰り返しです
解説:
nextを使うと、繰り返し処理中のnext以降の部分をスキップし、次の回に移る。
練習問題 loop_next.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
max = 10
sum = 0
for number in 1..max
if number % 2 == 0 # 偶数はスキップする
next
end
sum += number
end
printf("%dまでの奇数の合計は%dです。", max, sum)
結果:
10までの奇数の合計は25です。
解説:ループの中でif文を使って number の値を 2 で割った余りが 0 であれば next しているので 2、4、8、10 の時は sum は増えない。結果として、1 から 10 までの奇数だけの合計をとるプログラムになっている。
redoは、ループ条件のチェックを行わずにその回のループ処理をやり直す。
練習問題 loop_redo.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
number = 0
while number < 10
number += 1
if number % 2 == 0
printf("%2d - やり直します\n", number)
redo
end
puts number
end
結果:
1
2 - やり直します
3
4 - やり直します
5
6 - やり直します
7
8 - やり直します
9
10 - やり直します
11
解説:
制御構造を含めてこれまで習ったことを活用しているプログラムの例として、教科書の46ページの「マッチ棒取りゲームのプログラム」を作成し遊んでみよう。また、コードをゆっくり読んでゲームがどのように実装されているか確認すること。理解できない部分があれば授業で質問するか教科書での解説を読んでください。
練習問題 match_game.rb#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
left = 17 # 残っているマッチ棒の数
print "1から3本の範囲でマッチ棒を取ります。最後の1本を取った方の負け!\n"
while left > 0
printf("あなたの番: マッチ棒が%d本あります\n", left)
print "何本取りますか?(1~3): "
taken = gets.to_i
if taken < 1 || taken > 3 then
print "1から3の範囲で取って下さい.\n"
redo
end
left -= taken
if left < 1 then
print "最後の1本を取りました。あなたの負けです! やあい\n"
break
end
printf("わたしの番: マッチ棒が%d本あります\n", left)
com_taken = 3-taken+1
printf("私は%d本取りました。\n", com_taken)
left -= com_taken
if left < 1 then
print "最後の1本を取りました。わたしの負けです! くやちいい!!\n"
break
end
end
今回のプログラムでは、「if」や「while」から「end」までの行はそろって字下げが行われている。 これをインデントという。 条件が満たされている場合にどこからどこまでの処理が行われるかインデントによって見やすくなる。 特に上記のマッチ棒取りゲームのように、繰り返し処理の中にさらに条件分岐が含まれている場合など(このように制御構造が入れ子になっていることをネスト構造という)、インデントがなければプログラムが読みにくくなる。
emacsではそれぞれの行で[Tab]キーを押せばインデントを自動的に行ってくれる。 プログラムを書くときには[Tab]キーでインデントを行うことを心がけよう。
Ruby では半角の「#」(シャープ)から後ろの文字はコメントとして扱われる。 コメントは Ruby インタプリタからは無視されて実行されない。 変数の役割や複雑な処理の内容などをコメントで解説を書いておくと、後から読み返したり、他人が見たときに分かりやすい。
ただし、コメントを付けすぎるとプログラムが逆に読みにくくなることもあるので、コメントは必要最低限にしよう。
前回のbmi_calc.rbを改良し、BMI値によって評価を表示するプログラムを作成せよ
BMI < 18.5 の場合: UNDERWEIGHT
BMI >= 18.5 かつ BMI < 25 の場合: NORMAL
BMI >= 25 かつ BMI < 30 の場合: OVERWEIGHT
BMI >= 30 の場合: OBESE
ヒント:BMIがどのカテゴリーに当てはまるかはif・elsif・elseの構文を使って確認できる。
実行結果の例:
sime{c11xxxx}% ruby bmi_calc2.rb
身長をm単位で入力してください
1.75
体重をkg単位で入力してください
70
HEIGHT (m): 1.75
WEIGHT (kg): 70.00
BMI: 22.86 (NORMAL)
本文は下記の通り記入してください.
氏名: 苗字名前
学籍番号: C11xxxxx
ソースコード:
...
実行結果:
...
発展課題