目次

6.  制御構造

 

とうとう、「制御構造」の章まで来ましたね。プログラムを組もうとする人は必ずこれを 覚えることになります。 この章が前のメソッドの章と比べて短くて簡単だとしても これを知ることによってすべてのプログラムの可能性が開けてくるのです。 この章が終わったら、真にインタラクティブなプログラムが書けるようになるはずです。 これまでは、あなたのキーボードからの入力に応じて異なることをしゃべる プログラムを 作ってきましたが、この章からは入力に応じて異なることを実行する ようになります。 でもその前に、前段階として、プログラムの中でオブジェクトを比較することができるようになる 必要があります。つまり、、、

比較メソッド

この部分はさっさと進めて次の分岐節に行くようにしましょう。 そこで初めてクールな技が使えるのです。 さて、ひとつのオブジェクトがもうひとつに対して、より大きいかどうか、あるいは、 より小さいかどうかを知るには、> あるいは、< のメソッドを使います。 こんなふうに。

puts 1 > 2
puts 1 < 2
false
true

問題ないでしょう?(訳註:trueは真、falseは偽を表します。) これと同じように、あるオブジェクトがもうひとつのオブジェクト以上かどうか (あるいは...以下かどうか)は、>=メソッド(<=メソッド)を 使うとわかります。

puts 5 >= 5
puts 5 <= 4
true
false

そして最後に、2つのオブジェクトが、等しいか否かは==(等しいですか?) あるいは!=(異なっていますか?)のメソッドを使って知ることができます。 ===の2つを混同しないようにするのは重要です。 =は変数にどのオブジェクトを指し示すかを告げる(代入する)ためにあり、 ==は「これら2つのオブジェクトは等しいですか?」と質問をするためにあるのです。

puts 1 == 1
puts 2 != 1
true
true

もちろん文字列を比較することもできます。文字列どうしを比較した場合、 その文字的順序 、つまり辞書順という意味ですが、によって 比べることになります。辞書(アルファベットの順番の辞書)では catdogの前に来ます。なので、

puts 'cat' < 'dog'
true

でもこれにはちょっと落とし穴があります。コンピュータの中では 大文字が小文字よりも先に来ます。(これは、文字のフォントをどのように 保存するかにかかっています。たとえば最初に大文字全部、その後小文字が続く という具合です。) つまり、この方法だと'Zoo''ant'の前に来ることに なります。なので、本当の辞書において、どちらの単語が先に来るかを はっきりさせたいときには、文字列を比較しようとする前にはどちらの単語についても downcase(あるいはupcasecapitalize)を行っておく ことが必要です。

後ひとつ、分岐に進む前に知っておきたいことがあります。それは、 比較メソッドが'true''false'のような文字列を返しているのではなく、 特別なオブジェクトであるtruefalseを返しているのだということ です。(もちろん、true.to_s'true'を返します。 これがputsメソッドが'true'を表示している理由です。) truefalseは、以下の節で常に使われています。

分岐

分岐はシンプルな概念ですが強力です。実際、非常に単純なので、まったく説明しなくても いいと思うくらいです。それではお見せしましょう。

puts 'こんにちは, 君の名前は何かな?'
name = gets.chomp
puts 'こんにちは, ' + name + '.'
if name == 'Chris'
  puts '良い名前だね!'
end
こんにちは, 君の名前は何かな?
Chris
こんにちは, Chris.
良い名前だね!

でも、ほかの名前を入れると・・・

こんにちは, 君の名前は何かな?
Chewbacca
こんにちは, Chewbacca.

これが分岐です。もしifの後に来るものがtrueなら ifendの間のコードを実行します。 ifの後に来るものがfalseなら実行しません。簡単で単純。

上のプログラムで、私はifendの間を字下げしました。 こうすると、分岐をしているのがどこまでなのかがわかりやすくなります。 ほとんどのプログラマは、使っているプログラミング言語にかかわらずこれをします。 こういう単純なプログラムではありがたみはないように見えますが、 プログラムが複雑になってくると断然違ってきます。

しばしば、ある式がtrueの時にあることをして、falseのときに 別のことをしてくれるようなプログラムを書きたくなることがあります。 このためにelseがあります。

puts '私は預言者である.  名を告げなさい:'
name = gets.chomp
if name == 'Chris'
  puts 'おー、輝かしい未来が見えるぞよ.'
else
  puts '汝の未来は... おっと待った. いま何時だ!'
  puts '私は行かねばならない. 失敬する!'
end
私は預言者である.  名を告げなさい:
Chris
おー、輝かしい未来が見えるぞよ.

違う名前で試してみましょう...

私は預言者である.  名を告げなさい:
Ringo
汝の未来は... おっと待った. いま何時だ!
私は行かねばならない. 失敬する!

分岐は、コードをたどっていて分かれ道に到達したようなものです。 name == 'Chris'が成り立つ人の道を行くか、 あるいは(else)、そのほかの道を行くか、です。

そして、木の枝(branches)が枝分かれしているように、 分岐した先で、また分岐することも出来ます。

puts 'ハロー, 第7学年の英語の授業にようこそ.'
puts '私の名前はミセスガバードです. あなたのお名前は...?'
name = gets.chomp

if name == name.capitalize
  puts 'はい, お座りなさい, ' + name + '.'
else
  puts name + '? えーと, ' + name.capitalize + ' という意味ですね.'
  puts '自分の名前の綴り方くらいは知っていますね??'
  reply = gets.chomp

  if reply.downcase == 'yes'
    puts 'ふむ! よろしい、座りなさい!'
  else
    puts '出て行きなさい!!'
  end
end
ハロー, 第7学年の英語の授業にようこそ.
私の名前はミセスガバードです. あなたのお名前は...?
chris
chris? えーと, Chris という意味ですね.
自分の名前の綴り方くらいは知っていますね??
yes
ふむ! よろしい、座りなさい!

うまくいっていますね。先頭の文字を大文字にして(capitalizeして)試してみます...

ハロー, 第7学年の英語の授業にようこそ.
私の名前はミセスガバードです. あなたのお名前は...?
Chris
はい, お座りなさい, Chris.

時々、ifelse、そしてendの全部が どこにあるのか確かめようとして訳がわからなくなることがあります。 これに対して、私がやっているうまい方法はifを書くときに end同時に 書いてしまうというやり方です。 上のプログラムはそうやって書いたのですが、ここに、その途中段階 を示します。

puts 'ハロー, 第7学年の英語の授業にようこそ.'
puts '私の名前はミセスガバードです. あなたのお名前は...?'
name = gets.chomp

if name == name.capitalize
else
end

その後、間をコメント で埋めます。コメントというのは、 コード中にあって、コンピュータが完全に無視する文字です。

puts 'ハロー, 第7学年の英語の授業にようこそ.'
puts '私の名前はミセスガバードです. あなたのお名前は...?'
name = gets.chomp

if name == name.capitalize
  #  礼儀正しく.
else
  #  怒り出す.
end

#の後に来るものすべてはコメントとみなされます。 (もちろん文字列中でない限りですが。) で、私はその後、これらのコメントを実際に動くコードに置き換えていきます。 このようなコメントを残しておくのが好きな人もいますが、個人的には 「良く書かれたコードはそれ自身を語る」と思っています。 以前は私ももっとコメントを使っていたのですが、Rubyを「すらすらと」と使う ようになればなるほどコメントは使わなくなってきました。 実際、コメントを書くと結構時間を食われてしまいます。 これは個人的な選択です。 あなたも自分なりの(そして常に進化していく)スタイルを見つけることでしょう。 さて、私が採っている方法を続けると、次のステップはこんな感じになります。

puts 'ハロー, 第7学年の英語の授業にようこそ.'
puts '私の名前はミセスガバードです. あなたのお名前は...?'
name = gets.chomp

if name == name.capitalize
  puts 'はい、お座りなさい, ' + name + '.'
else
  puts name + '? えーと, ' + name.capitalize + ' という意味ですね.'
  puts '自分の名前の綴り方くらいは知っていますね??'
  reply = gets.chomp

  if reply.downcase == 'yes'
  else
  end
end

またしてもifelse、そしてend を同時に書きました。この方法はコードの中で「今どこにいるのか」を 覚えておくのにかなり役に立ちます。その上、ifelseの間のような小さい場所に意識を集中できるので、 プログラムを書くのをよりやさしくさせてくれます。 もうひとつ、この方法をとる利点はコンピュータがどの段階でもプログラムを 理解できるということです。どの未完成バージョンのプログラムでも 実行させることが出来ます。未完成ではありながら実行可能なのです。 プログラムを書いている時点で、このようにテストを行い、 どんなふうに出来たのかと、これから何が必要なのかを確認するのに役立てて います。すべてのテストがうまくいったら、ここまで出来たというのが わかります。

このようなコツは、分岐を含むプログラムを書くときに役に立ちますが、 それだけではなくて、他にもある主な制御構造に対しても役立ちます。

ループ

プログラミングではしばしば、コンピュータに同じことを何度も何度も繰り返し 行わせたくなることがあると思います。これこそがコンピュータの得意技 ですよね。

コンピュータに同じことを繰り返し続けるように指示する時は、それを どうやって止めるのかも必ず指示する必要があります。 コンピュータは飽きることを知らないので、もし止まる方法を教えないと、 自動的に止まることはありません。ということで、無限の繰り返しが 起こらないように、ある特定の条件が正しい(true)の間(while)だけ、 プログラムのある部分を繰り返すように、コンピュータに告げるようにします。

command = ''

while command != 'bye'
  puts command
  command = gets.chomp
end

puts 'また来て下さいね!'

Hello?
Hello?
こんにちは!
こんにちは!
はじめまして.
はじめまして.
あぁ...なんて素敵なんでしょう.
あぁ...なんて素敵なんでしょう.
bye
また来て下さいね!

これがループです。(出力の最初に空の行が1行あるのに気が付きましたか? これは最初のgetsの前にある最初のputsから出ています。 この最初の行をなくすにはどう変えたら良いでしょうか? やってみましょう。 それは上のプログラムと比べて最初の空行が消えている以外、まったく 同じ動作をしますか?)

ループを使えばあらゆる面白そうなことができそうでしょう? でも、ミスったときはちょっと問題を起こします。 コンピュータが無限ループに陥ってしまったらまずいですよね。 これが起こったなと思ったら、落ち着いて、 Ctrlキーを押しながらCを押しましょう。

ループを使っていろいろ遊んでみる前に、仕事を楽にするための、 2,3のことを学んでおきましょう。

論理を少し

もう一度、分岐を使った最初のプログラムを見てみましょう。 今度は、私の妻が来てプログラムを試してみたとします。 このままだと、プログラムは彼女に「良い名前だね!」とは言わないわけです。 私は彼女の気持ちを損ねたくはありません。(でないと、カウチで寝ることに・・) ということで、直しましょう。

puts 'こんにちは, 君の名前は何かな?'
name = gets.chomp
puts 'こんにちは, ' + name + '.'
if name == 'Chris'
  puts '良い名前だね!'
else
  if name == 'Katy'
    puts '良い名前だね!'
  end
end
こんにちは, 君の名前は何かな?
Katy
こんにちは, Katy.
良い名前だね!

はい、これで動きます... でもあまりかっこいいプログラムとはいえませんね。なぜでしょう? プログラミングに関して私が覚えたルールの中でベストなものは、 DRY ルールと呼ばれるものです。これはDon't Repeat Yourself(あなた自身を 繰り返しちゃいけない) の頭文字です。 多分、なぜそれがそんなに良いルールなのかについて、私は小さな本くらい書けると思います。 先ほどの場合だと、puts '良い名前だね!'の行を2回繰り返して書いています。 なぜこれがそんなにオオゴトなんでしょうか?たとえば、ここを書き直すときにスペルミスを してしまったとしたらどうでしょうか? あるいはこの両方の行で'良い名前''素敵な名前'に変えたくなったとしたらどうでしょう? 私は怠け者です。覚えてました? 基本的に、'Chris''Katy'の文字を入力されたときに プログラムに同じことをして欲しいなら、私は本当に同一のこと をしてもらいたい のです。つまり、こうです。

puts 'こんにちは, 君の名前は何かな?'
name = gets.chomp
puts 'こんにちは, ' + name + '.'
if (name == 'Chris' or name == 'Katy')
  puts '良い名前だね!'
end
こんにちは, 君の名前は何かな?
Katy
こんにちは, Katy.
良い名前だね!

だいぶ良くなりました。上のことを実現するために、私はorを使いました。 このような論理演算子 にはこの他にandnotがあります。 このような演算子を使うときには、いつも一緒に括弧を使うというのは、良い考えです。

i_am_Chris  = true
i_am_Purple = false
i_like_food = true
i_eat_rocks = false

puts (i_am_Chris  and i_like_food)
puts (i_like_food and i_eat_rocks)
puts (i_am_Purple and i_like_food)
puts (i_am_Purple and i_eat_rocks)
puts
puts (i_am_Chris  or i_like_food)
puts (i_like_food or i_eat_rocks)
puts (i_am_Purple or i_like_food)
puts (i_am_Purple or i_eat_rocks)
puts
puts (not i_am_Purple)
puts (not i_am_Chris )
true
false
false
false

true
true
true
false

true
false

この中で、変だと思うものがあるとしたら、or(または)でしょう。 英語では(訳註:日本語でも)、私たちは"or"という言葉をよく 「これかあれかのどちらかで、両方ではない」という意味で使います。 たとえば、あなたのママが「パイかケーキをデザートに食べていいわよ。」 と言ったとしたら、両方とも食べてもいいということにはならない ですよね。でも、コンピュータはorを「あれかこれか両方か」の 意味に使うのです。(別の言い方で言うと、「少なくとも一つは正しい」 ということです。)これがコンピュータがママより楽しい理由です。

練習問題

"99本のビールが壁に..." 遠足などでよく歌われる古典的な童謡の歌詞、"99 Bottles of Beer on the Wall" を 出力するプログラムを書いてみましょう。 (訳註:マザーグースの歌なのですが、日本ではあまりメジャーではないので、 出力例をあげておきます。)

99 Bottles of beer on the wall
99 Bottles of beer
Take one down and pass it around
98 Bottles of beer on the wall

98 Bottles of beer on the wall
98 Bottles of beer
Take one down and pass it around
97 Bottles of beer on the wall

…

3 Bottles of beer on the wall
3 Bottles of beer
Take one down and pass it around
2 Bottles of beer on the wall

2 Bottles of beer on the wall
2 Bottles of beer
Take one down and pass it around
1 Bottle of beer on the wall

1 Bottle of beer on the wall
1 Bottle of beer
Take one down and pass it around
0 Bottles of beer on the wall

• 耳の遠いおばあちゃんのプログラムを書いてみましょう。 おばあちゃんに何を言っても(何をタイプしても)、叫ばない限り (つまり、全部大文字でタイプしない限り)、 は?! もっと大きな声で話しておくれ、坊や! と返事をします。もし叫んだときは、彼女はあなたの言葉を聞いて (少なくとも聞いた気がして)、 いやー、1938年以来ないねー! と大声で返事をします。 プログラムにちょっと真実味 を持たせるため、 1930年から1950年のランダムな数字で毎回違う年を叫ぶようにしましょう。 (この部分はオプションです。メソッドの 章で、Rubyの乱数発生法の節を読んでいたら簡単でしょう。) あなたはBYEと叫ぶまでおばあちゃんとの会話から逃れられません。
ヒント: chompをお忘れなく! Enter付きの 'BYE'はついていない 'BYE'とは 違うものです。
ヒント2:あなたの書いたプログラムのどの部分が 何度も何度も繰り返し実行されるべきなのかを良く考えてみましょう。 そのすべてを whileループの中に入れます。

• 上で作った、「耳の遠いおばあちゃんのプログラム」を拡張しましょう。 おばあちゃんはあなたに行って欲しくないのです。 あなたがBYEと叫んでもおばあちゃんは聞こえないふりをします。 BYEを3回連続で 叫ばないといけないように変更してみてください。 BYEと3回叫んでも 連続していない限り、おばあちゃんとの会話は続くようになっているか プログラムをテストして確認してみましょう。

• うるう年。 開始の年と終わりの年を聞いて、その間にあるすべてのうるう年を (もし開始や終了の年がうるう年だったらそれも含めて)表示するプログラムを書きましょう。 うるう年は(1984年とか2004年のように)4で割り切れる数の年です。ただし、100で割り切れる年は うるう年ではなくて (たとえば1800年や1900年)、さらに、それは 400で割り切れない限り です。(つまり、1600年や2000年はうるう年です。) (確かに、このルールは紛らわしいです。でも7月が冬の真ん中に来てしまうのはもっと紛らわしいと 思いませんか?このルールがなかったらそうなったかもしれないのです。)

ここまでやったら少し休憩しましょう。すでにたくさんのことを学んできました。 おめでとう!! コンピュータにこんなにたくさんのことを指示できるようになったな んて、驚きでしょう? 後少しの章でどんなことでもプログラムができるようになりますよ!! んー、ちょっとこれは言い過ぎかもしれませんけれど、ループや分岐を覚える前には できなかったことで、新しくできるようになったこと全部を振り返ってみることは 良いことだと思います。

さて。一段落したところで、次の新しい種類のオブジェクトについて学んでいきましょう。 それは他のオブジェクト(物)の並びを保持しているオブジェクト、つまり 配列です。

 

目次