[CG実習 >  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回でした

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

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


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

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

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

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

false,nil,FALSE,NIL
偽でないもの全て

FALSEとNILは,それぞれ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, つまり「存在しない」を返します.

if式 --- 条件分岐

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


12    if guess == x      # 正解!
13      print "正解です\n"
14      guess_ok = true
15    elsif guess < x    # 予想の方が小さい
16      print "もっと大きい数です\n"
17    else               # 予想の方が大きい
18      print "もっと小さい数です\n"
19    end

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

guessとxが等しいとき 13,14行めが実行される.その後20行めに移る
guessがxより小さいとき 15行めが実行される.その後20行めに移る
guessがxが大きいとき 18行めが実行される.その後20行めに移る

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式 --- 繰り返し

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


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

このwhile式では,「!guess_ok」が真である, すなわち「guess_ok」が偽である限り, 10行め〜20行めが繰り返し実行されます.

なお,11行めでは, 前回は,2行に分けて書いていた入力処理を1行でまとめて行っています.


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

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

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

while式の構造

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


  while  条件式
    「条件式」が真であるときに繰り返す部分(while節)
  end

while式の構造

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

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

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


 8  # 予想が正しくない限り繰り返す
 9  while !guess_ok
10    print "予想? "
11    guess = gets.to_i  # getsで得られた文字列を整数に変換する
12    if guess == x      # 正解!
13      print "正解です\n"
14      guess_ok = true

なお,実習で使うバージョンのRubyでは,while式は何も値を返しません. while式を代入式の右辺とするとエラーとなります. 最新のバージョンのRubyでは,while式は値を持ちます.

イテレータ

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

Rubyの他の言語とは異なる特徴として, イテレータ(iterator)があります. イテレータも,繰り返し処理を実現するための仕組みですが, 明示的な繰り返しの仕組みを書かなくても済むようになっています. この実習でも,最初のRubyスクリプト ( myfirst.rb ) で配列を扱うのにイテレータを利用しています. イテレータについては, 次回の「配列の説明」で詳しく述べます

条件式

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を返すメソッドがあります. そのようなメソッドの名前には,慣例として,最後に「?」がついています.


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

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

サンプルプログラム

制御構造を用いたサンプルプログラムをいくつか紹介します.

[CG実習 >  Rubyによるプログラミングの基礎 >  制御構造]