[プログラミング演習(Ruby) >  条件に基づく処理]

条件に基づく処理

さて,この演習ではプログラムを記述するのにemacsを使っています. emacsでRubyのプログラムを編集すると,予約語,定数,コメント等々にはそれぞれに応じた色がつけられます. この機能はプログラムの構造を見やすくするのに大変役立ちます. ところで,この機能はもちろんプログラムで実現されています(Rubyではなく,Emacs Lispという言語が使われています). このプログラムを便宜上「色付けプログラム」と呼びましょう.

emacsでRubyプログラムのファイルを開いたとき, 色付けプログラムはRubyプログラムを読んでその内容に応じて別々の色付けをする必要があります. つまり,内容次第で別々の処理を行う必要があります. また,そのような色付け処理を最初の行から最後の行まで各行について繰り返し行う必要があります.

前回までは最初から1行ずつ順番に最後まで1本道で処理が進むプログラムのみを 扱っていました. しかしこの「色付けプログラム」の例で示したように一般なプログラムでは次のような処理の実行制御が不可欠です.

条件分岐 条件によって異なる処理を行う.
(プログラムの進む道が分岐・合流する)
繰り返し (条件によって)同じ処理を何度も繰り返す
(プログラムの進む道が同じところを何度も回る)

これらの処理では,プログラムの流れが条件に基づいて制御されるようになっています. ここではこのような条件に基づく処理について説明します.

もくじ

  1. 例題
  2. プログラムの仕様
  3. 条件分岐
  4. 繰り返し
  5. プログラム
  6. 真偽値 --- 「正しい」と「正しくない」
  7. if式 --- 条件分岐
  8. while式 --- 繰り返し
  9. イテレータ
  10. 条件式
  11. サンプルプログラム

例題

1桁の数をデタラメに決めて,その数を予想して当てるプログラムを作る. 正解するまで予想を繰り返す. 予想が正しくないときは,正解が予想より大きいか小さいかを表示する. 正解したら,それまでに何回予想したかを表示する.

プログラムの仕様

プログラムの流れは,おおよそ次の通りになるでしょう.

  1. 0〜9の整数xをデタラメに一つ決めておく.
  2. 最初に予想回数を0にしておく.
  3. 予想を入力する.
  4. 予想とxを比べる.
    • 予想が正解ならば,「正解です」と表示する.
    • 予想の方が小さい場合は,「もっと大きな数です」と表示する.
    • 予想の方が大きい場合は,「もっと小さな数です」と表示する.
  5. 予想した回数を1増やす.正解なら次に進む,正解でないなら3に戻る.
  6. 予想した回数を表示して終了する.

条件分岐

例題のプログラムにおいては, 予想が正しいかどうか,正しくないなら, 予想の方が大きいのか小さいのかを判断して, 別々のメッセージを表示することが必要です.

このようにあることが正しいのかあるいは正しくないのかによって別々の内容を実行する条件判断による分岐は,プログラムに不可欠です. Rubyで条件判断をどのように実現するかについては,例題のプログラムの説明の中で詳しく述べます.

繰り返し

例題のプログラムにおいては, 正解がでるまで予想を繰り返す必要があります. 条件判断とともにこのような繰り返し処理もプログラムには不可欠です.

Rubyで繰り返し処理をどのように実現するかということについても,やはり例題のプログラムの説明の中で詳しく述べます.

プログラム

例題のプログラムをRubyで記述すると, 以下のようになります. なお,各行の左端の数字は説明のために付けた行の番号で, プログラムには,この行番号は含まれません.

guess.rb
[行番号つきプログラムを別のウィンドウで開く] [行番号なしのプログラム]

このプログラム(guess.rb)は次のように実行します.

$ ruby guess.rb
1桁の秘密の数を当てて下さい 予想? 5
もっと小さい数です
予想? 2
正解です
予想回数は2回でした

真偽値 --- 「正しい」と「正しくない」

例題のプログラムでは, 予想が正しいのか正しくないのかによって, 異なった動きをします. それでは,プログラムにおける「正しい」,「正しくない」とはどう定義されるのでしょうか.


12  guess_ok = false   # 予想が正しいかどうか
13  n = 0              # 予想回数
14  x = rand(10)       # 1桁(=0以上10未満)のデタラメな数

12行めでは,guess_okという変数を用意しています. コメントにあるように,この変数は予想が正しいかどうかというデータを表すのに用いられています. 代入されているオブジェクトはfalse(偽)です. これはRubyで「正しくない」という意味を持つオブジェクトです. 反対に「正しい」はtrue(真)で与えられます.
さて12行めの時点では,まだ一度も予想をしていません. 「guess_ok=false」と代入しておきます. すなわち最初は「予想は正しくない」としておきます(そうしないとあとでうまく動作しません).

なお14行めではxに0〜9のデタラメな数を代入しています. rand(n)は0以上n未満の整数をデタラメに(ランダムに)発生します.

ところでRubyではtrue,false以外のオブジェクトも全て,真(正しい)と偽(正しくない)に分類されます.

false,nil
偽でないもの全て

nilについては,すぐあとで説明します. なお0も真ですので,0を偽とみなす他のプログラミング言語に慣れた人は注意して下さい.

nil

nilとは,空(カラ)という意味で,Rubyでは「何もない」, つまり「オブジェクトが存在しない」という意味を表す特別なオブジェクトです.

  str="This is a string object."
  str.index("is")  # ==> 2
  str.index("are") # ==> nil

この例では,文字列オブジェクトのindexというメソッドを使っています. indexメソッドは引数で指定された文字列がオブジェクトの文字列の中に現われていれば,その出現位置(先頭の文字を0として何文字目か)を返します. 引数で指定された文字列が現われなければ,nil,つまり「存在しない」を返します.

以後さまざまなところでnilを使います.

if式 --- 条件分岐

例題のプログラムは, 予想が入力されたら,これを秘密の数と比較して, 比較の結果によって異なった動きをします. このような処理を行っているのが12〜19行のif式です.


22    if guess == x      # 正解!
23      print "正解です\n"
24      guess_ok = true
25    elsif guess < x    # 予想の方が小さい
26      print "もっと大きい数です\n"
27    else               # 予想の方が大きい
28      print "もっと小さい数です\n"
29    end

この部分は guessとxの大小関係で次のように実行されます.

guessとxが等しいとき 23,24行めが実行される.その後30行めに移る
guessがxより小さいとき 25行めが実行される.その後30行めに移る
guessがxが大きいとき 28行めが実行される.その後30行めに移る

if式の構造の詳細は以下で説明します. また条件を表現する式については,「条件式」の節で説明します.

if式の構造

もっとも単純なif式は,次のような構造になっています.

  if  条件式
    「条件式」が真であるときに実行する部分(if節)
  end
if節のみのif式の構造

ifに続いて判断すべき条件の式を記述します. 条件が正しい場合(条件式が真であるとき)にのみ, ifにつづくendまでの部分を実行します. この部分をif節といいます. 一方,条件が正しくない場合(条件式が偽であるとき)には,if節は実行しないで,先に進みます.

条件が正しくない場合(条件式が偽であるとき)にも 何かを実行する場合には,ifとendの間にelseを入れます.

  if  条件式
    「条件式」が真であるときに実行する部分(if節)
  else
    「条件式」が偽であるときに実行する部分(else節)
  end
if節とelse節を持つif式の構造

if〜elseの部分がif節となり,条件が正しいときのみ実行されます. else〜endの部分は条件が正しくないときのみに 実行されます.この部分をelse節といいます.

さらに,複数の条件を組み合わせることもできます. そのような場合は,二つめ以降の条件式は,elsifにつづいて記述します.

  if  条件式1
    「条件式1」が真であるときに実行する部分(if節)
  elsif 条件式2
    「条件式1」が偽であり「条件式2」が真のときに実行する部分(elsif節)
  else 
    「条件式1」が偽であり「条件式2」も偽のときに実行する部分(else節)
  end
一般的なif式の構造

この例の場合は, if〜elsifがif節であり,ifにつづく条件式1が真のときのみ実行されます. elsif〜elseがelsif節であり, ifにつづく条件式1が偽で,elsifにつづく条件式2が真のときのみ実行されます. else〜endがelse節であり,条件式1も条件式2も偽のときにのみ実行されます.

elsif節は必要に応じていくつでも並べることができます. else節は,必要であれば最後に一つだけ作ることができます. else節は,(存在すれば)すべての条件が偽のときのみ実行されます.

elsif節,else節は必ずしも必要ではありません. いずれにしても,設定されている条件によって, if節,(0個以上の)elsif節,(0あるいは1個の)else節のうちのどれか一つのみが 実行されることになります.

節(if節,elsif節,else節)の中には,任意の式を書くことができます. すなわち,節の中にまたif式を書くこともできます. これにより,複雑な条件判断を伴ったプログラムを記述することができます.

なお,if式は式ですから値を持ちます. したがって,if式を代入の右辺とすることもできます. if式の値は実行した節で最後に評価した式の値です. 節が空の場合は,nilとなります.

while式 --- 繰り返し

例題のプログラムでは, 正しい予想が行われるまで同じことを繰り返します. これを行っているのが,19〜31行のwhile式です.


18  # 予想が正しくない限り繰り返す
19  while not guess_ok
20    print "予想? "
21    guess = gets.to_i  # getsで得られた文字列を整数に変換する
22    if guess == x      # 正解!
  : 
:
29  end 30  n = n + 1 # 予想回数を1増やす. 31  end

このwhile式では「not guess_ok」が真である(notは否定を意味します), すなわち「guess_ok」が偽である限り, 20行め〜30行めが繰り返し実行されます.

「not guess_ok」は「!guess_ok」とも書けます.

なお,21行めでは, 入力処理(キーボードからの文字列の読み込みと整数への変換)を1行でまとめて行っています.


21    guess = gets.to_i  # getsで得られた文字列を整数に変換する

ここではgetsで得られる文字列オブジェクトのto_iメソッドを起動して,その返り値の整数オブジェクトをguessに代入しています.

以下でwhile式の構造を説明します.

while式の構造

while式は,次のような構造になっています.

  while  条件式
    「条件式」が真であるときに繰り返す部分(本体)
  end
while式の構造

whileにつづく条件式が真であれば,whileからendまで(本体)を実行します. endに達したら,whileの先頭にもどって,条件式をあらためて評価します. ここで再び条件式が真であれば,while節を再度実行します. 再びendに達したら, さきほど同様にwhileの先頭にもどって,条件式をあらためて評価します. このあとも, さらに同様に,whileの条件式が真である限り,whileの本体を繰り返し実行します. whileの条件式が偽となったら,本体を実行せず, endの次から実行を続けます. このような繰り返しの部分では, 同じ部分を何度もぐるぐると巡ることから,while式のことを whileループ(loop;輪)ともいいます.

whileの本体には,任意の式を書くことができます. したがって, 例題のプログラムのようにwhileの中にifが現われることもありますし, whileの中にwhileが現われることもあります(二重ループ).

ところで,例題のプログラムでは, 「guess==x」という条件が成立したとき, すなわち予想が正しかったときに,14行め 「guess_ok = true」が実行されます. ここで,whileの条件式「!guess_ok」(not guess_ok)は正しくなくなりますので, 正しい予想が得られたときに,繰り返しを終了することが分かります.


18  # 予想が正しくない限り繰り返す
19  while not guess_ok
20    print "予想? "
21    guess = gets.to_i  # getsで得られた文字列を整数に変換する
22    if guess == x      # 正解!
23      print "正解です\n"
24      guess_ok = true

なおwhile式は通常nilを返します.

イテレータ

条件分岐や繰り返しは,さまざまなプログラミング言語に備わっています. 繰り返しとしてはwhileがもっとも一般的です.

繰り返しのための仕組みとして,Rubyにはイテレータ(iterator)というものもあります. この実習でも,最初のRubyスクリプト ( myfirst.rb ) で配列を扱うのにイテレータを利用しています.

イテレータは本来「ブロック付きメソッド」というのが一般的である. ここでは繰り返し処理用の「ブロック付きメソッド」であることを意識してイテレータという名称を使っている. じつは,もともとRubyではこのような構造はすべてイテレータと呼ばれていた.しかし必ずしも繰り返し処理を行わない場合があることから「ブロック付きメソッド」と呼ばれるようになったという経緯がある.

ここでイテレータの例として,決まった回数の繰り返しを行うtimesを紹介します. timesは,整数のメソッドの一つです.

  5.times do |i|
    print " "*i
    print "hello!\n"
  end

このプログラムを実行すると次のように表示されます.

hello!
 hello!
  hello!
   hello!
    hello!

この他にも似たようなイテレータとしてuptodowntostepがあります. 詳しくはRubyの「リファレンスマニュアル」を参照して下さい. リファレンスマニュアルはポータルページからリンクしています(授業ポータルのページ).

イテレータについては「配列」の資料で詳しく説明します.

条件式

if式while式では条件を表す式(条件式)を利用します. あらゆるオブジェクトには真偽値が与えられていますので, どんな式でも条件式となり得ます.ここでは演算子の形で利用される条件判断のためのメソッドを紹介します.

比較演算子

条件式では二つのオブジェクトの大小関係を調べる演算がしばしば使われます. 大小比較の演算子には,次のようなものがあります.

x < yxはyより小さい
x <= yxはyより小さい,あるいは等しい
x == yxとyは等しい
x >= yxはyより大きい,あるいは等しい
x > yxはyより大きい
x != yxとyは等しくない

「=」(等号)は代入を表しますので, 「等しい」は「==」(等号二つ)と書くことに注意して下さい. 他にも「<=>」という大小関係により整数値を返す演算子や オブジェクトの同一性を判定する「===」もあります.

論理演算子

複数の条件を一つの式に組み合わせたい場合があります. そのようなときには,論理演算子を用います.

A && B A and B AかつB
A || B A or B AまたはB
!A not A Aではない

同じ意味の演算子が二つずつあります. 「&&,||,!」と「and,or,not」では,他の演算子との優先順位が異なります.

その他

文字列や配列などのオブジェクトには, オブジェクトに関する条件判断を行って,true/falseを返すメソッドがあります. そのようなメソッドの名前には,慣例として,最後に「?」がついています.

  [1,3,5,7,9].include?(5) # ==> true
  "abcde".empty?          # ==> false

1番目の例のinclude?は, 引数として渡したオブジェクトが配列に含まれるかどうかを判定するメソッドです. 2番目の例のempty?は, 文字列が空であるかどうか, つまり文字列中の文字数が0かどうかを判定するメソッドです. このような例としてとりあげると,何に使うのだろうと思うかもしれませんが, いずれも有用なメソッドです.

サンプルプログラム

以下にサンプルプログラムをいくつか紹介します.

[プログラミング演習(Ruby) >  条件に基づく処理]