制御構造


プログラムは通常、1行目から順番に実行されて、終了する。この流れを変えるのが制御構造である。制御構造によって、プログラムの一部を実行させなかったり、同じ処理が何度も繰り返されるようにさせたりすることができる。以下はよく使う制御構造である条件分岐繰り返し処理について説明する。

詳細についてはRubyリファレンスマニュアルを参照してください。


条件分岐(if文)

文法:

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文を使うことが多い。

文法:

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文

文法:

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文

文法:

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メソッド

文法:

回数.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番目の繰り返しです


break, next, redo

while文などの繰り返し処理において、途中で繰り返し処理を中断したり、 次の回に飛ばしたり、再度やり直したりしたい場合がある。そのような時は、繰り返し処理の中でbreaknextredoを用いる。

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

ソースコード:
...

実行結果:
...

 発展課題 

  1. 前回の電卓プログラムを修正し、4種類の計算をすべて行うのではなく、ユーザーがどの演算を行うかを選択できるようにすること。
  2. コンピューターと対戦できるじゃんけんゲームを作ること。

目次