ここではRubyというプログラミング言語について学びます

Rubyの基本

Rubyは変数の型の宣言を必要としない、オブジェクト指向言語の一つで、 インタプリタ(つまりコンパイルを必要としない)で動くプログラミング言語です。

Rubyのプログラムを実行するにはいくつかの方法があります。

私の勧めは最後の方法です。 irbを使うと、一つ一つのコマンドの実行結果がどのようなものか、 すぐにわかるからです。ちゃんと一つ一つのコマンドが動くことが確かめられたら、 ファイルにすべて書き出し一番目か二番目の方法で実行する、というのがよいでしょう。

最初のRubyプログラム

「こんにちは」を画面に表示するプログラムを書いてみましょう。 まずはTera Padを用いて
print "こんにちは"
と打ち込んでみてください(この資料からコピーしてTeraPadにペーストするのでも構いません)。
[命令の最後に;をつけなくても良い?]
プログラミング言語Cなどでは、命令文の最後にセミコロン ; をつけなければなりません。 しかし、Rubyでは不要です。もっともセミコロンが機能しないわけではありません。 一行にいくつかの命令を書きたい場合には、命令の区切りとしてセミコロンが使えます。

そして、first.rbという名前をつけて『ファイルに保存」してください。 これがデスクトップに保存されたとすれば、次のようにしてまずワーキングディレクトリをデスクトップに移し、
cd .NT\_Desktop
そして次のようにrubyプログラムを使って実行させます:
ruby first.rb
このように実行すれば、次のように表示されるはずです。
こんにちは

printというのがrubyの関数(メソッドともいいます)の一つで、画面に引数の値を表示する、というものです。そして、"こんにちは" の部分がその引数です。Rubyは、数だけではなく、このような「文字列」を扱うことが簡単にできます。ここで文字列とは、 " "、つまり引用符号(ダブルクォート、もしくは二重引用符と言います。)で囲まれている文字の並びのことです。

今度はirbを使ってみましょう。この場合、ワーキングディレクトリがどのようにセットされていても構いません。
irb
すると、次のような表示がなされるでしょう:
irb(main):001:0>
001は、irbを起動して最初の行であることを示します。次の0はトップレベル(関数やプログラムの途中ではない)ことを表しています。そこで、次のように入力し、最後にEnterキーを入力してください。
print "こんにちは"
すると、次のような表示がえられるでしょう。
こんにちは=>nil
irb(main):002:0>
ここで「こんにちは」の部分がprint関数による表示です。=>の次のnilは、print関数が実行結果としてnilを返したことを示しますが、これについては当分は気にしなくて結構です。

同じ事は、次のようにしてもできます。

a = "こんにちは"
print a
ここでは、文字列「こんにちは」を変数aに代入し、printを使ってaの値を表示させてみました。ここで2つのことに気づくことでしょう。一つ目は、変数aをいきなり(変数の宣言をすることなく)使っていること、2つ目は「a = "こんにちは"」を入力したときに なにか変な表示がされるということです(ここではそれが何かはわざと書いていません)。 [変数]
Rubyの変数は英文字小文字から始まり、半角文字のアルファベットか数字か _ (アンダーバー)という記号から作られていなければなりません。 先頭を大文字にすると、変数ではなく「定数」という扱いになります。定数は、値の変更が許されていないことが変数との違いです(できない、というわけではありませんが、Warning(警告)が発生します)。

このようにRubyの特徴の一つは、変数は(定数も)宣言なしに使え、しかもどんな型のオブジェクトも代入できる、ということです。たとえば、続けて次のようにしても怒られません。 (以下の例は、今、文字列を値とした変数aに数を代入しようとするものです)

a = 12345679 * 9
print a*8 

「a = "こんにちは"」を入力したときに変な表示がされたことについて説明します。 ここで表示されたものが示しているのは、「こんにちは」という日本語文字がコンピュータでどのように表現されているかを示しています。注意してみると、¥と数字3つの並びが10個現れていることに気づくでしょう。¥と数字3つは、8進法を用いた1バイトのデータを表しています。そして 2バイトで日本語の文字1文字を表しているのです。だから、「こんにちは」という5文字の日本語文字にたいして、表示されるのは10個の数(今説明したように、\202のように¥に続く3個の数字が一つの数を表しています)の並びだったのです。

\202\261...のような表示のままでは何が表示されたのかわからないので、気持ちが悪いという人もいるでしょう。こうなった理由は、日本語の文字コードとして何が使われているかrubyが把握していなかったからです。 そこで、次のようにやり直してみてください:

$KCODE = "s"
a = "こんにちは"
今度はちゃんと(=> に続けて)「こんにちは」という表示がされましたね。 [$KCODE]
この例のように、$KCODEの値を"s"にセットすれば、文字コードとしてShift-JISが使われていることをRubyに教えることができます。ちなみに、"e"ならEUC、"u"ならUTF-8が使われていることを教えることになります。

また、$KCODEも変数の一つです。$KCODEのように先頭が$から始まる変数は、普通の変数がローカル変数として扱われるのに対して、大局変数の扱いとなります。

ここでirbを終了させる言葉を書きます。それはquitです(exitでも同じことができます)。

文字列と数

これまでは、プログラムの中で、文字列という種類の対象だけを扱ってきましたが、 ここでは、プログラムで数も扱うことにしましょう。 数に対しても、文字列について今まで学んできたことがあてはまります。 つまり、変数への数の代入や変数からの数の取り出しなども全く同じような操作でできます。 ただし、数は文字列と違って、プログラムの中ではそのまま書けば意味を持ちます。 (注意: 引用符で囲むと、数ではなく文字列として扱われます。)[\n]

\ という記号は、ダブルクォートで囲まれた文字列の中で、nやtのような記号と組み合わされると特別な意味を持ちます。 ここで用いられた\nは「改行コード」を表します。これをprintすると、何も表示されずに改行が発生します。また、\tは「タブ」を表します。その効果は実際に次のようなプログラムを実行してためしてみてください(このように\tの位置によって表示されるスペースの個数が変わることに注意)。
print "ここにはタブがありません\n"
print "ここに\tタブが2つ\tあります\n"

num = 1000
print  num, "\n"

式と値

これまで、変数を単独で扱ってきましたが、変数や文字列、数を要素にして、 式を作ることができます。
例えば、次のプログラムの中の num1 + num2 は式の例です。

num1 = 1000
num2 = 2000
wa = num1 + num2
print  wa, "\n"

このように、プログラムの構成要素として「式」があります。 簡単な式を組み合わせてより複雑な式が構成できるというのは、一般の数式の場合と同じです。

[#, コメント]
Rubyでは文字列の中でなければ、#以降はコメントとみなされます。 つまり#から書かれているのは「人間のためのメモ」であって、Rubyとしての処理は行われません(言い換えれば、読み飛ばしされます)。
ただ # がコメントという機能をもつのは、行末までです。もっと長くコメントを書きたい場合は、 次のように、コメントの部分を=begin=endで挟みます。
=begin
これはコメントを数行にわたって書く場合の例です。
作ったプログラムには、プログラムの説明や動かし方のようなコメントをつける習慣をつけましょう。
=end
num1 = 1000
num2 = 2000
kekka = (num1 + num2) * num2    # * は掛算を表す記号です。
print  kekka, "\n"

式は変数と同様に「式全体」で値を持ちます。 値を持つプログラムの構成要素を式だと言ってもよいでしょう。
文字列を対象にした式も作れます。
たとえば次のように、文字列を + で結合したものも式です。この式を実行すると、それらが結合 された文字列が式の値になります。

kotoba = "おはよう、" + "太郎さん" 
print  kotoba, "\n"

もう少し詳しく、プログラムの実行過程を見ていきましょう。 上のプログラムを実行すると、まず1行目の命令が実行されるのは当然ですね。 このような実行されるプログラムの構成単位をと呼びます。 命令が言葉の集まりとして構成されているからです。 一行目は、変数への代入を行う命令ですから、「代入文」と呼ぶこともあります。 代入文は、等号(=)の右辺の式を実行し、その結果の値を等号の左辺にかかれた変数に代入するものです。 さて、この代入文は今までに出てきた単純な代入文とは違って、右辺が式になっています。

kotoba = "おはよう、" + "太郎さん"
右辺は
"おはよう、" + "太郎さん" 

となっていて、文字列が「 + 」の記号で繋がっている式です。 このように文字列を演算の対象とする「 + 」は二つの文字列をつなげるという働きをします。 この式を実行すると、二つの文字列が繋がって一つの文字列、

"おはよう、太郎さん" 

が出来上がり、これが式の値になります。 そして「 = 」を使う代入文は右辺の式の値を左辺の変数に代入するということになりますから、 "おはよう、太郎さん" がkotobaという変数に代入され、この変数の値になります。

キーボード入力

次のプログラムでは、キーボードからの入力を受け取るための命令文が使われています。 gets.chomp がその命令文です。 この命令文が実行されるとキーボードからの入力を待つ状態になります。 文字が入力されるとプログラムの実行を再開し、受け取った文字列をプログラムの 中で扱うことができるようになります。 次のプログラムでは、入力された文字列を aisatsu という変数に代入しています。
まずはこのプログラムをコピーして実行し、キーボードからの入力を受け取れている ことを確認してみましょう。プログラムを実行し、「あいさつ:」と表示された後に 「こんにちは」などの文字列を入力して、Enterキー を押してください。 (コマンドプロンプトで日本語を使うときは Altキー を押しながら 半角/全角キーを押します。 ちなみに半角文字モードに戻すのも、Altキー を押しながら 半角/全角キーを押します)

print "あいさつ:"
aisatsu = gets.chomp

print "\n"
print aisatsu, "。お元気ですか?\n"
[今の例をirbで実行する人のために]
irbで今の例を実行すると、「あいさつ:」の後に改行が入ってしまうので、 どこで入力したらよいか少しわかりにくくなります。 そこで、ここでは次のようにしてください。 
print "あいさつ:"; aisatsu = gets.chomp

print aisatsu, "。お元気ですか?\n"

課題1

課題1-1

「キーボード入力」で示したプログラムを参考として、 以下のような実行結果になるプログラムを作成しなさい。 ただしここで、オレンジ色の文字はキーボードからの入力を表しています。

名前:中京太郎
あいさつ:こんにちは

中京太郎さん、こんにちは。
お元気ですか?

課題1-2

次のように、キーボードから会計金額と人数を入力すると、一人当たりの割り勘金額を 計算してくれるプログラムを作成しなさい。(オレンジ色の文字はキーボードからの入力を 表しています。キーボードから数の入力を受け取る方法は、下の解説を参照してください。)

お会計:10000
人数:3

お一人様3333円です。
不足金額は1円です。

[gets.to_iでキーボードから数の入力を受け取る]

キーボードから数の入力を受け取るときは次のような命令文を使用します。 キーボードから入力された数字(注意:これは「文字」の並び)を、 プログラムの中で数(注意:これは「数値」)として扱うことができるようになります。

kaikei = gets.to_i

注意: 実行時には半角数字で整数を入力してください。

[割算]

割算を計算するときは / を使用します。

print 10000 / 3
3333

数が変数に代入されている場合も、同じように計算することができます。

kaikei = 10000

print kaikei / 3
3333

ここで、なぜ 3333.3333333 にならないのか、と思った人がいるかもしれません。 これについては後で述べますが、Rubyでは「整数」と「小数点数をふくむ数(これを 浮動小数点数と呼びます)」とは違う扱いがされているからです。 小数点以下の数も表示したければ、

print kaikei / 3.0
というように、整数の3ではなく、浮動小数点数の3.0で割り算してみてください。

[余りを求める計算]

割算の余りを求める計算をするときには % を使用します。

print 10000 % 3
1

課題1-3

次のプログラムを実行すると何がどのように表示されるのか予測して、答えなさい。 その後、実際に実行して、予測と合ったかどうかも報告しなさい。


num = 10

num = num + 10
print num, "\n"

num = num + 10
print num, "\n"

num = num * 10
print num, "\n"

num = num / 3
print num, "\n"
[掛算]

掛算を計算するときは * を使用します。

print 1000 * 3
3000

数が変数に代入されている場合も、同じように計算することができます。

num = 1000

print num * 3
3000

課題1-4

次の実行結果のように、長方形の面積を計算するプログラムを作成しなさい。 (オレンジ色の文字はキーボードからの入力を表しています。)

長方形の面積を計算します。
縦と横の辺の長さを入力してください。
縦:10
横:20
長方形の面積は200です。

縦と横の辺の長さは、キーボードからの入力を受け取るようにします。 キーボードから数の入力を受け取る方法については、 [キーボードから数の入力を受け取るには] を参照してください。

キーボードから数の入力を受け取るときは、次のような命令文を書きます。

変数 = gets.to_i    #キーボードからの数の入力を受け取り、変数に代入する。

実は上の命令文では、キーボードから受け取った文字列を数に変換しています。 .to_iのiは「整数」を意味するIntegerのiです。 .to_iには、文字列を整数に変換する働きがあります。

ここでは数を2つ受け取る必要がありますので、キーボードから数の入力を受け取るための命令文を、2つ書く必要があります。 2つの数を受け取ったら、それらを使って長方形の面積を計算し、結果を表示しなさい。 掛算の計算には * を使用します。

課題1-5

次のプログラムを実行すると、ディスプレイに何が表示されるか、予測しなさい。 次に、実際にプログラムを実行してみて、自分の予測と適合したかどうかを報告しなさい。 自分の予測と合わなかった場合は、どうして合わなかったのか理由を考え、それを報告しなさい。
ただし、まず、次のgets.to_iの解説を読んで、理解してから、取り掛かること。

print "年齢を数字でキーボードから入力してください。"
nenrei = gets.to_i #ここで、20を入力することにする。
print "あなたの年齢は", nenrei, "才です。\n"

[解説:to_iの役割]

mojiretsu = "20"
kazu = 20
print (mojiretsu == kazu)
上のプログラムを実行すると、falseが表示されます。
ちょっと紛らわしいことのように思うかもしれませんが、"20"と20は違うものです。 "20"は文字"2"と文字"0"が結合したものに過ぎませんが、("ab"のようなものです。)
20は数そのものです。この違いは大事です。 ですから、次のような足算はできません。つまり、文字列と、数を足し合わせることはできません。
mojiretsu = "20"
kazu = 20
goukei = mojiretsu + kazu
print goukei,"\n" 
つぎのように、文字列をまず数に直してからならできるのです。
mojiretsu = "20"
kazu = 20
goukei = mojiretsu.to_i + kazu
print goukei,"\n" 
つまり、to_iは、文字列という値を数という値に変換する役割を持っていると言えます。

[解説:gets.to_iとgets.chomp]

これまで、
gets.chomp
というのはやりました。この命令を実行すると、 コンピュータはキーボードから、入力を「待つ」状態に入ります。
実際、そこで、止まっていると言ってよいでしょう。
次に、キーボードから文字を打ち込んで、最後にEnterを入れると、それをきっかけにして、 コンピュータは動き出します。Enterを押すと実は改行コードつまり"\n"が送られ、実際には この改行コードをきっかけにして、動き出します。(何かのきっかけが必要です。) そして、gets.chompという式が値を持つことになり、その値がキーボードから打ち込んだ文字列+"\n"です。 "\n"は余計なので、これを取るためにchompがついているのです。
ただ、to_i は文字列の最後に改行コードがあっても無視します。そのため、 gets.chomp.to_i と書かずに gets.to_i ですませることが出来るのです。


条件分岐

条件分岐とは、次のように状況を判断して、適切な反応ができるようなプログラムの仕組みです。 次のコンピュータとの対話例を見てください。

コンピュータ:あなたの年齢はいくつですか?(年齢を数字で入力してください。)
年齢:19
コンピュータ:残念ですが、まだ、お酒を飲んではいけない年齢ですね。

年齢に20を入力すると、

コンピュータ:あなたの年齢はいくつですか?(年齢を数字で入力してください。)
年齢:20
コンピュータ:おめでとう。もう、お酒を飲める年齢ですね。

このように、入力によって違う応答を返すことができます。

このようなやりとりを実現するためには、入力された年齢(これは数として扱います)によって、 コンピュータに異なる振る舞いをさせるプログラムを作成する必要があります。 そのためには次のような、ifの構文を使った、条件分岐の仕組みが必要です。

if (条件判定式)
  
  
 ...
else
 
  
 ...
end
まずは実際に条件分岐の仕組みを持つプログラムを動かしてみて、上の対話例と同じような振る舞いをするか確認しておきましょう。
次のプログラムをコピーしてファイルに保存し、実行して振る舞いを確かめなさい。 どのような年齢を入力するとどんな応答が返ってくるか確かめて、わかったことを報告しなさい。 いろいろな年齢を入力して試してみるとよい。

print "コンピュータ:あなたの年齢はいくつですか?(年齢を数字で入力してください。)\n"
print "年齢:"
nenrei = gets.to_i

if (nenrei < 20)
  print "コンピュータ:残念ですが、まだ、お酒を飲んではいけない年齢ですね。\n"
else
  print "コンピュータ:おめでとう。もう、お酒を飲める年齢ですね。\n"
end

条件分岐は強力な仕組みです。 コンピューターが人間のための強力な道具になるのは、 このように場面に応じて適切に動作するようにプログラムできるからである、 と言っても言い過ぎではないでしょう。 知的な振る舞い、つまり適切な場面で適切な動作をさせる根源にこの「条件分岐」があるのです。
ifで作られる条件分岐のプログラムの、基本の形は次のようなものです。

if (条件判定式)
 #上の括弧の中の条件判定式の実行結果が真のときだけ以下の文が次々に実行される。
   
  
 ...
else
  #上の括弧の中の条件判定式の実行結果が真でないときだけ以下の文が次々に実行される。
 
  
 ...
end
先にあげたプログラムがどのように動作するのか、年齢に19という数を入力した場合を例に、ていねいに追いかけてみましょう。
print "コンピュータ:あなたの年齢はいくつですか?(年齢を数字で入力してください。)\n"
print "年齢:"
nenrei = gets.to_i  #キーボードから19という数が入力されると...

if (nenrei < 20)
  print "コンピュータ:残念ですが、まだ、お酒を飲んではいけない年齢ですね。\n"
else
  print "コンピュータ:おめでとう。もう、お酒を飲める年齢ですね。\n"
end

ifの括弧の中の条件判定式が問題です。 この括弧の中の「条件」が判定され、合っていればすぐ下の部分が実行されます。 条件に合っていない場合は、elseの下に書かれた部分が実行されます。

条件判定式について詳しく見てみましょう。 条件判定式の部分が、実際に実行されることに注意してください。 実行されると値を持つのが式でした。 条件判定式も式ですので、実行されるとその値が求められます。 上の条件判定式の場合、nenrei < 20 という式がまず実行され、「19は20より小さい」という意味に合致しますので、trueという値がこの式の値になります。 trueというのは、条件判定式が正しいことを表す値です。 反対に、条件判定式が間違っている場合はfalseという値で表されます。

式の値として、これまでは文字列だけを扱ってきましたが、 これからは真を表すtrue偽を表すfalseも扱うことになります。これらは「真偽値」と呼ばれます。文字列と数とは違う「第3の」種類の値です。 今度はこの部分だけを、詳しく見てみましょう。

課題2

課題2-1

条件判定式が実際に値を持つことを確かめてみましょう。
次のプログラムを実行すると何が表示されるか考え、予想を書きなさい。
次に、実際にプログラムを実行してみて、自分の予想と適合したかどうかを報告しなさい。
予想と違う結果になった場合は、自分の予想がどう間違っていたか説明し、どのように考えてそのような結果になったのか説明しなさい。

nenrei = 19
print nenrei < 20

print "\n"

nenrei = 21
print nenrei < 20

[ヒント1]

ここまでで扱った3種類の値(文字列、数、真偽値)を、printを使って表示してみましょう。

print "こんにちは", "\n"    #文字列
print 100, "\n"             #数
print true, "\n"            #true
print false, "\n"           #false

真偽値のtrueとfalseは数と同じように、そのままtrue、falseと表示されます。

こんにちは
100
true
false

[ヒント2]

次のような文字列の連結や、数の計算も「式」の一種です。

#文字列の連結
print "こんにちは、" + "中京太郎" + "さん。", "\n"

#数の計算
print 100 * 2, "\n"

式は実行されると値を持ちますので、printによって表示されるときにはその値が表示されます。

こんにちは、中京太郎さん。
200

条件判定式も「式」ですので、何らかの値を持ちます。 上と同じように、printの実行時には式の持つ値が表示されます。

nenrei = 19
print nenrei < 20

print "\n"

nenrei = 21
print nenrei < 20

2つの条件判定式がそれぞれどのような値を持つか考えてみましょう。 わからない人はもう一度、課題1-1の前の解説を読んでみましょう。

比較演算子

if文の条件判定式で使用する < や > のことを比較演算子と呼びます。 比較演算子には次のような種類があります。

 
  
a == baとbは等しい
a != baとbは等しくない
a < baはbより小さい
a > baはbより大きい
a <= baはb以下
a >= baはb以上

課題2-2

次のようなプログラムを実現しなさい。
キーボードから入力した年齢が20才以上の場合は、「あなたは選挙権を持っています。」 と返事をし、年齢が20才より小さい場合には「あなたはまだ選挙権をもっていません。」 というメッセージを出力するプログラムを作成しなさい。
具体的な応答の例です。
年齢はいくつですか?年齢を数字でキーボードから入力し、最後にエンターキーを押してください。
年齢:19
あなたはまだ選挙権をもっていません。

ランダムな数の生成

rand(n)は乱数を発生させる命令です (そしてrandは、乱数を発生させる 関数、nはその引数(ひきすう)と言います)。
ここで引数のnには正の整数か、正の整数を値とする変数が入ります。 rand(n)を実行すると、0から、n-1までのどれかの整数がランダムに作り出されます。 ですから、rand(3)では 0,1,2の3つの数がどれかが発生するのであって、3は発生されないことに注意してください。
ランダムな数の集まりのことを乱数ともいいますが、乱数はプログラミングではとても重要で役に立つものです。 実際、乱数を使ったプログラムがたくさんあります。
次の課題で、さいころの目を出すプログラムをこのrand関数で実現してみましょう。

課題2-3

次のように、実行するたびごとにサイコロの出る目がランダムに変わるプログラムを実現しなさい。
H:>ruby program.rb
さいころを振ります!
コロ,,,  #0.3秒づつ待つ。
コロ,,, #0.3秒づつ待つ。
コロ,,, #0.3秒づつ待つ。
         #0.5秒待つ。
さいころの目は3でした。

H:>ruby program.rb
さいころを振ります!
コロ,,,  #0.3秒づつ待つ。
コロ,,, #0.3秒づつ待つ。
コロ,,, #0.3秒づつ待つ。
         #0.5秒待つ。
さいころの目は5でした。
[ヒント:0から5ではなく、1から6までの目を出すには]
rand(6) + 1
とすればよいだけです!

課題2-4

これまでに学んだことを応用して、次のようなサイコロゲームを作成しなさい。
#当たった時
さいころゲームの始まりです!
次に振ったさいころの目を当てましょう。
次にでる目はなんですか?
予測:5
では振ります。
コロ,,,  #0.3秒づつ待つ。
コロ,,, #0.3秒づつ待つ。
コロ,,, #0.3秒づつ待つ。
サイコロの目は? #0.5秒待つ。
5 でした!
大当たりです! 超能力をもっていますね。
#はずれた時
さいころゲームの始まりです!
次に振ったさいころの目を当てましょう。
次にでる目はなんですか?
予測:5
では降ります。
コロ,,,  #0.3秒づつ待つ。
コロ,,, #0.3秒づつ待つ。
コロ,,, #0.3秒づつ待つ。
サイコロの目は? #0.5秒待つ。
2 でした!
残念でした。また挑戦してくださいね。

※ 普通に実行するとおそらく「予測」という漢字が文字化けしてしまうはずです。 正しく表示するためには、Rubyのプログラムを次のようにして実行する必要があります。

H:>ruby -Ks kadai2-4.rb 

半角大文字のKと小文字のs(漢字コードはShift_JISの意。)

「予測」のように正しく表示できない文字が出てきたときは、上の方法で実行することを覚えておいてください。

[作成のヒント1:プログラムの流れ]

さいころの目はrand(6)+1で作り出せます。
ゲームの参加者の予測はキーボード入力から取り出せます。
上の二つが同じかどうかの判定とそれに従った分岐はif文の条件分岐でできます。
num1とnum2が同じかどうかは、(num1 == num2 )という条件判定式でできます。

[作成のヒント2:変数をうまく使う]

変数に値(数など)をとっておけば、後で必要になった時に使えます。

[作成のヒント3:文字列と数の違いに注意]

gets.chompでは、文字列が入るだけで、数の判定ができません。

より複雑な条件分岐(if文)を理解する

if文はプログラムを場面に応じて適切に動作させるためになくてはならない ものであり、プログラミングの基本の一つであることは上で述べました。 より 複雑なif文を持つプログラムの理解と作成に挑戦してみましょう。 上で学んだように、if文の基本的な構造は次のようなものです。
if (条件判定式)
    文1
    文2
    ...
else
    文100
    文101
    ...
end
ifとelseとendというキーワードと条件判定式と実行される文の集まり二つから構成されています。 前に出した同じ具体例で、もう一度確かめておきましょう。
nenrei = 21

if (nenrei >= 18)
  print "あなたは車の免許を取ることができます。"
else
  print "あなたはまだ車の免許を取ることができる年齢に達していません。"
end
ifの次の括弧の中式は条件判定式です。この括弧の中の式が実行され、値が 真であれば、次の行の文1、文2などが順番に実行されて終わります。 条件判定式を実行した結果の 値が偽であれば、elseの下の実行文100、実行文101などが順番に が実行されます。 つまり、条件判定式の値によって、実行される文(の集まり)が違うところが大事なところです。

さて、基本は同じですが、もう少し複雑なif文を見てみましょう。 if文全体も一つの文ですから、次のようにif文の実行式1のところに、if文を 入れることで、つぎのようにより複雑な分岐を実現することができます。 プログラムの書き方として、内側のif文は何文字か字下げ して、見やすくするようにすることが大事です。(インデントと言います。)

if (条件判定式)
  if (条件判定式)
    文
    文
    ...
  else
    文
    文
    ...
  end         # 内側のifを終了する
else
  文
 文
  ...
end     # 外側のifを終了する
具体的には、次のような例に応用できます。
print "年齢を数字でキーボードから入力してください。"
nenrei = gets.to_i

if (nenrei >= 18)
  if (nenrei >=20)
    print "あなたは車の免許を取ることができます。\n"
    print "あなたはお酒も飲むこもできます。\n"
    print "よかったですね!\n"
  else
    print "あなたは車の免許を取ることができます。\n"
    print "しかし、あなたはお酒を飲むことは法律で禁じられています。\n"
    print "残念でしたね!\n"
  end      # if (nenrei >= 20)
else
  print "あなたは残念ながらまだ車の免許を取ることができる年齢に達していません。\nお酒も飲んではいけません。\n"
end    # if (nenrei >= 18)

if文は自由に組み合わせて構成できますから、さまざまな変形の形があります。 elseがないものも可能です。

if (条件判定式)
  文
  文
  ...
end

また、次のように、if文の中にif文のつながりがあってもよいことになります。

if (条件判定式)

   if (条件判定式)
     文
     文
     ...
   end

   if (条件判定式)
     文
     文
     ...
   else
     文
     文
     ...
   endend

課題2-5

次のプログラムがどのように振舞うのか、プログラムを一歩一歩追いかけて予測しなさい (実行して調べてははだめです)。 その後、実際に実行して、予測と合ったかどうか報告しなさい。

spacer = "          "
print spacer

kazu = gets.to_i

if (kazu == 1)
  print "☆","\n"
else
  if (kazu == 2)
      print "☆☆","\n"
  else  
    if (kazu == 3)
        print "☆☆☆","\n"
    else
      if (kazu == 4)
        print "☆☆☆☆","\n"
      else
        print "★","\n"
      end       # if (kazu == 4)
    end      # if (kazu == 3)
  end    # if (kazu == 2)
end  # if (kazu == 1)

elsif文

今のプログラムでは else のあとに if文が現れていました。図示すると次のようになっていました。
if (kazu == 1)
  文1
else
  if (kazu == 2)
    文2
  else  
    if (kazu == 3)
        文3
    else
      if (kazu == 4)
        文4
      else
        文5
      end       # if (kazu == 4)
    end      # if (kazu == 3)
  end    # if (kazu == 2)
end  # if (kazu == 1)
この場合は、変数kazuの値がなにかによって実行する文が選ばれています。
このようにelseの中にif文が埋め込まれる場合はよくあります。 Rubyではこのような場合、次のように elsif 文によって見やすく(?)書くことを許しています。
if (kazu == 1)
  文1
elsif (kazu == 2)
    文2
elsif (kazu == 3)
        文3
elsif (kazu == 4)
        文4
else
        文5
end    # end of if 

課題2-6

課題2-5のプログラムをelsif文を使って書き直しなさい。 またこれを実行して課題2-4のプログラムと同じ結果を返すことを確認しなさい。

[参考:case文]

課題2-5のプログラムはkazuの値によって実行する文が異なり、その選択をif文を使って実現していました。 このように、ある変数の値によって実行する文を変える場合、Rubyでは次のようにcase文を使って書くことができます。 そして、この方がよりプログラムがわかりやすくなると考えられています。
case kazu
  when 1   # (kazu == 1) と同じ効果
      文1
  when 2   # (kazu == 2) と同じ効果
     文2
  when 3   # (kazu == 3) と同じ効果
     文3
  when 4   # (kazu == 4) と同じ効果
     文4
  else
     文5
end   # end of case 


繰り返しの基本を学ぶ

プログラミングをする上で頻繁に使う最も重要な基本パターン は「繰り返し」の構造だと 言っても言い過ぎではないでしょう。 コンピューターが強力な道具であるのは、それが高速であるため ですが、その高速性を生かして役に立つ道具にするためには、これから説明する 「繰り返し」の構造が本質的な役割を果たしています。 現実のほとんどのプログラムが繰り返し構造を使っていると言ってもよいでしょう。

次のように、「こんにちは」を10回表示するプログラムを考えてみましょう。

こんにちは
こんにちは
こんにちは
こんにちは
こんにちは
こんにちは
こんにちは
こんにちは
こんにちは
こんにちは

これまでは、print文を10個書く必要がありました。

print "こんにちは\n"
print "こんにちは\n"
print "こんにちは\n"
print "こんにちは\n"
print "こんにちは\n"
print "こんにちは\n"
print "こんにちは\n"
print "こんにちは\n"
print "こんにちは\n"
print "こんにちは\n"

繰り返し構造を実現するforwhile を使うことで、上のプログラムを次のように短く簡潔に書くことができます。

なぜ、これでうまくいくのか、その仕組みを理解しましょう。 仕組みを理解することは大事です。 そうしないと、大事なくり返しの構文を自由自在に使いこなせないからです。

2種類の繰り返し: forとwhileの特徴

Rubyではいろいろな種類の「繰り返し」の構文が用意されています。 その中でforwhile はとても重要です。これは2つの違う考えに基づく繰り返しを実現するからです。

forは、「ある決まった回数だけ」 (もしくは決まった個数の対象に対してだけ)、命令を繰り返します。 今の例はprint命令を10回繰り返すという問題でしたから、for文を使うには 最適の問題でした。

whileは、「ある条件が成り立っている間」だけ 命令を繰り返します。 今の例はprint命令を10回繰り返すという問題でしたから、while文を 使うにはあまり適さない問題でした。

for文の仕組み

forというキーワードを使った繰り返しの構文は次のような形をしています。
for 変数 in 変数の取りうる最小値..最大値
     文1
     文2
     ...
end  # for 

for文を実行すると、次のように動作します。

1. まず変数の値として「変数の取りうる範囲の最小値」が与えられる。
2. 変数の値が「変数の取りうる範囲の最大値」より大きければ、for全体の実行を終える。
3. endまでの文1, 文2, ...が順番に実行される。
4. 変数の値に1が足され、プログラムの実行が2.に戻り、もう一度同じことが繰り返される。
先ほどの「こんにちは」を10回表示するfor構文を使ったプログラムが働く上で大事なことは、 繰り返しを制御する変数が必要なことです。変数iの値の変化を表すよう書きなおした 次のプログラムを実行させてみてください。

for i in 1..10
  print i, "回目のこんにちは\n"
end    # for 

ここで変数としてiを使いましたが、変数として使えるものならなんでも構いません。 ただこの変数がないと、繰り返しはうまく機能しません。 なお、変数iのとりうる範囲を1..10と書いていますが、これはもちろん 最小値が1,最大値が10であることを表しています。 ちなみに、単に10回「こんにちは」をprintするだけでしたら、 最小値が1001で最大値が1010でも構いません(変数iがとる値は1001から1010までの10通り)。 実際、次のようにも書けます(が、プログラムがわかりにくくなりますので、普通はやりません)。

for i in 1001..1010
  print "こんにちは\n"
end  # for 

課題3-1

次の結果を出力するプログラムを、for文を使って実現しなさい。
1
2
3
4
5
6
7
8
9
10

課題3-2

次の結果を出力するプログラムを、for文を使って実現しなさい。
20
18
16
14
12
10
8
6
4
2

[ヒント:数を減らすにはどうしたらいい?]

for文では数を1つずつ増やすことしかできません。 そこで表示する数を徐々に減らしていくにはどうしたらよいか、悩んだかもしれません。

いろいろなやり方がありますが、計算によって数を減らしていく方法を紹介しましょう。

10 - i = 10    # i=0 の場合
10 - i =  9    # i=1 の場合
10 - i =  8    # i=2 の場合
10 - i =  7    # i=3 の場合
10 - i =  6    # i=4 の場合
...

while文の仕組み

whileというキーワードを使った繰り返しの構文は次のような形をしています。

while (条件判定式)
  文1
  文2
  ...
end    # while 

whileを実行すると次のように動作します。

1. まずif文と同様、条件判定式が実行され、その値が取り出される。
  1-1. 条件判定式の値がの時、
         その下の文1、文2などが順番に実行される。(この辺りはif文と同じです。)
         endまで来ると、プログラムの実行場所がwhileの先頭に戻る。
         (1. に戻り、条件判定式がもう一度実行され、同じことが繰り返される。)
  1-2. 条件判定式の値がの時、
         何もしないで、while全体の実行を終える。(2. に進む。)
2. endの次の文から実行を再開する。

先ほどの「こんにちは」を10回表示するプログラムをwhile構文を使って書くには、 表示される回数を数えるための変数が必要になります。以下ではその変数として iを用いています。そして、while文の前に変数iに0を与え、 表示が終わるごとにiに1を足すようにしています。変数iの値の変化に着目してください。

i = 0          #iの値は0。
while(i < 10)
  print "こんにちは\n"
  i = i + 1    #iの値を1つ増やす。
end  # while 

10回繰り返して、そこで終わりになるのは、繰り返すたびごとにiの値が1ずつ増えていることと、 条件判定式 (i < 10) のおかげです。 10回繰り返すまでは、iの値は10より小さいので、条件判定式が真となり、繰り返しを行うことになります。 10回以上になると、条件判定式が偽となりますので、whileの実行を止めることになります。
このように、回数を記憶するための変数 (このような変数をカウンター変数と呼びます)には、 whileの繰り返しに入る前に0などの数を代入しておく必要があります。 この変数の働きがないと、while文を使ってある一定の回数繰り返す、というプログラムはうまく機能しません。

課題3-3

次の結果を出力するプログラムを、while文を使って実現しなさい。
1
2
3
4
5
6
7
8
9
10

課題3-4

次の結果を出力するプログラムを、while文を使って実現しなさい。
20
18
16
14
12
10
8
6
4
2

課題3-5

次の実行結果に示されるように、 最小値と最大値をそれぞれキーボードから入力し、その間の数をすべて足した 合計を返すプログラムをfor文とwhile文、それぞれを使って書きなさい。 (オレンジ色の文字はキーボードからの入力を 表しています。キーボードから数の入力を受け取る方法は、 ここを参照してください。)

数の合計を計算します。数を入力してください。
最小値:10
最大値:30
10から30までの数の合計は420です。

for文とwhile文のどちらのほうが書きやすかったでしょうか?またその理由は何でしょう。

課題3-6

nを正の整数とした時、階乗n!を計算するプログラムを作成しなさい。
但し、次の例のように、nはキーボードから入力するものとし、 階乗を計算した結果を以下のように出力するようにしなさい。
以下はnが100の時の例です。

コンピュータ:自然数nの階乗の計算を行います。nをキーボードから入力し、Enterキーを押してください。
n:20
結果:20の階乗は2432902008176640000です。

[ヒント:階乗の意味]

階乗の意味は次の通りです。

n! = n * (n-1) * (n-2) * ... * 3 * 2 * 1 
例
5! = 5 * 4 * 3 * 2 * 1 = 120


配列

ここでは配列について学びます。 配列はプログラミングには欠かせない最も重要な基本要素、 基本的な「データ構造」の一つです。 配列をマスターしない限り、現実のプログラミングはできないと言っても言い過ぎではありません。 繰り返しがコンピューターの力のもとだといいましたが、配列と一緒に使うことで、 コンピューターはますます強力になると言ってもよいでしょう。

それでは、配列とはどんなものでしょうか。たくさんのものをしまったり出したりできる、便利な入れ物です。ここまでは変数と同じように、入れ物として働いているだけですが、変数と違って、一つだけではなく、たくさんのものを入れたり出したりして処理することができます。 変数の集まりであると考えることもできます。 変数に値を入れることができるということは、変数がいろいろな値を取れるということになります。 (変数という言葉の由来です。)配列は複数の変数の集まりと見ることができ、したがって、複数の値を扱うことができるのです。 変数は変数名によって保持している値を扱いますが、 配列では、次に述べるような形で、名前で直接値を扱うのではなく、指標(インデックス)といわれる数によって、 値を扱うことができるのです。これが変数をとても便利に使える大きな理由の一つでしょう。 このようなことが何を意味するのか、下の例で一歩、一歩見て行きましょう。

配列の特徴を複数の変数を使う場合と対比してしてみましょう。いつくもの名前と挨拶を扱うプログラムを、配列を使わないで、変数だけで作ってみましょう。 まず、用意した nameA,nameB,nameC,nameD,nameEという5個の変数に、 名前を表す文字列を代入しておきます。それを次々に出力していくことにします。

nameA = "太郎"
nameB = "紘美"
nameC = "克規"
nameD = "倫哉"
nameE = "隆弘"

#ここまでで、変数への名前の代入ができました。
#これから後は出力です。

print "こんにちは、", nameA, "さん\n"
print "こんにちは、", nameB, "さん\n"
print "こんにちは、", nameC, "さん\n"
print "こんにちは、", nameD, "さん\n"
print "こんにちは、", nameE, "さん\n"
このプログラムを実際に、実行してみてください。

今度は、変数ではなく、配列の中に、上の5個の名前を入れます。 まず、nameという変数を用意します。準備として、その中に、空の配列とを入れておきます。 これは、次のような代入文でできます。

name = [ ]  #変数nameに空の配列をまず用意しておく。
配列は、Rubyだけでなく、多くの言語で[ ]を使って表すことが普通です。 次が肝心なところですが、以下のようにすることで、 名前を表す文字列を順番に配列の中に入れて行くことができます。
name[0] = "太郎"  #配列の0番目の要素に"太郎"という文字列を代入する。
name[1] = "紘美"  #配列の1番目の要素に"紘美"という文字列を代入する。
name[2] = "秀樹"  #以下同様。
name[3] = "倫哉"
name[4] = "隆弘"
この例は、変数nameの中の配列の0番目から4番目までのところにそれぞれ名前を代入しています。 数を使って区別していることが重要です。この数のことを 配列の指標(インデックス)と言います。 ここで指標は0以上の整数であることに注意してください。 これは、配列が0番から始まるということであり、 「配列の一番最初の要素の指標が0」ということです。 そして、ここでの命令はみな
変数名[指標] = 値
という形をしています。 ここで、
print name[2], "\n"
を実行すると、どんな出力結果が出てくるでしょう?
秀樹
が出てきます。name[2]に秀樹を代入してあるので、当然ですね。

課題4-1

以下では、nameという変数の中の配列には名前を順番に入れておきます。 プログラムを実行すると、何が出力されるのか予測して報告しなさい。 そのあと、実際に実行して、予測と合ったかどうか報告しなさい。 予測と合わなかったら、その理由も報告すること。

name  = []

name[0] = "太郎" 
name[1] = "紘美" 
name[2] = "秀樹" 
name[3] = "倫哉"
name[4] = "隆弘"

i = 3
print name[i], "\n"

配列がプログラミングの強力な道具になるのは、数(指標)によって配列の内容を取り出せるからです。 forやwhileの繰り返し文と組み合わせて使うことで配列は特に便利になります。 次の課題を行うことで、そのことを見てください。

課題4-2

次のプログラムを一歩一歩追いかけて、実行すると、何が出力されるのか予測して報告しなさい。 そのあと、実際に実行して、予測と合ったかどうか報告しなさい。 予測と合わなかったら、その理由も報告すること。


name  = []
name[0] = "太郎" 
name[1] = "紘美" 
name[2] = "秀樹" 
name[3] = "倫哉"
name[4] = "隆弘"

i = 0
while (i < 5)
  print "名前=", name[i], "\n"
  i = i + 1
end   # while 

前のプログラムと比べるとこのプログラムでは、 print文を5回書かずにたった一回だけ書けばよいことになり、 プログラムが簡潔になっています。配列の中の要素の数がどんなに大きくなっても、 whileで書かれているプログラムの中心部分は同じです。

実際に、配列の要素を増やしてみましょう。 配列にたくさんのものを一度に入れたいときは、次のように一行に書くこともできます。 要素同士は(半角文字の)コンマ , で区切られていることに注意してください。

nameData = ["太郎","紘美","秀樹","倫哉","隆弘","秀樹","花子","秀美","英俊","正樹","一彦","泉"] 

i = 0
while (i < 12)
  print "名前=", nameData[i], "\n"
  i = i + 1
end    # while 
このようにwhileを使えば、配列がどんなに大きくなっても、 プログラムを変えずに配列の内容を書き出すことができます。

さらに、配列に次に上げる.sizeを使うともっとやりやすくなります。 .sizeというのは、配列の要素の数を返せという命令です。 次のように、配列を表す変数もしくは配列そのものとsizeをピリオドで結んで使います。

ary = ["太郎","紘美","秀樹","倫哉","隆弘","一彦","花子","秀美","英俊","正樹","一彦"] 
print ary.size, "\n"
print  ["太郎","紘美","秀樹","倫哉","隆弘","一彦","花子","秀美","英俊","正樹","一彦"].size,"\n"
このプログラムを実行すると、11が表示されます。この11は配列の要素の数です。 このように配列の要素の数を自分で数えなくても計算機が答えてくれます。 この.sizeを使って、前のプログラムを書き換えてみましょう。
nameData = ["太郎","紘美","秀樹","倫哉","隆弘","一彦","花子","秀美","英俊","正樹","一彦"] 

i = 0
while (i < nameData.size)
  print "名前=", nameData[i], "\n"
  i = i + 1
end      # while 

先のプログラムと違うところは、 nameData.sizeを使っているところだけです。 nameDataという変数と"."でつないでいるところに注意してください。 変数の中の配列の要素の数(サイズ)を値として得ることができます。 (ここでは11です。) ですから、上のプログラムのwhileは配列の要素の数だけ、繰り返すのです。

課題4-3

今度は、次のような出力がでるような、プログラムを配列とwhileを使って作成 しなさい。
こんにちは、太郎さん
こんにちは、紘美さん
こんにちは、秀樹さん
こんにちは、倫哉さん
こんにちは、隆弘さん

配列には、文字列に限らず何でも、入れることができます。 (変数に何でも入るのと同じです。)数を扱ってみましょう。

data = [2,4,7,3,9,6,4,4,10,23,1,1,1,7,8,6,25,30,30,9] 

課題4-4

次のプログラムを実行すると、何が表示されるのか予想し、その予想を報告しなさい。
data = [2,4,7,3,9,6,4,4,10,23,1,1,1,7,8,6,25,30,30,9] 

print data.size,"\n"

print data[0], "\n"
print data[4], "\n"
i = 5
print data[i], "\n"
print data[i+3], "\n"

課題4-5

whileを使って、次の配列の内容をそのまま、以下のように出力するプログラムを作成しなさい。
data = [2,4,7,3,9,6,4,4,10,23,1,1,1,7,8,6,25,30,30,9] 
2
4
7
3
#(中略)
30
9

[ヒント:data[i]]

iなどの変数を使って、配列の指標と繰り返しの指標を連動させることがこつです。
典型的なパターンです。

...
while (i < data.size)
  ...
  .. data[i]
  ...
end   # while 

課題4-6

次のプログラムは配列の要素の数を合計して結果を出力しようとして、 作ったものですが、完全なものではありません。 実際に実行しながら、誤りを見つけて、正しいプログラムを完成させなさい。 まず、このまま実行してみて何が表示されるのか調べて、それから考えてみてください。
data = [2,4,7,3,9,6,4,4,10,23,1,1,1,7,8,6,25,30,30,9] 

i = 0
sum = 0
while ( i < data.size)
  sum = data[i]
  i = i + 1
end    # while 
print "配列の合計は", sum, "です。\n"

出力結果は次のようになります。

配列の合計は190です。

配列にfor文を使う。

配列はfor文を使うと、簡単に扱えることがあります。
次のような形で扱います。

data = [e0, e1, ... , en]
for ele in data
  文1
 文2
  ...
end   # for 

for文を理解する上でのポイントは、inの前の変数です。代入文が明示されていませんが、 配列の中からその要素が一つ一つ取り出されては、eleに代入され、実行されることを繰り返します。

何回繰り返し実行されるのかという回数は、配列の要素の数と同じになるのは当然です。whileを使った場合と比べてforを使う繰り返しは、指標(インデックス)を使わずに配列の要素を直接扱えるという特徴があります。

課題4-7

課題4-3で作成したプログラムをfor文を使って書き直しなさい。

課題4-8

課題4-6で作成したプログラムをfor文を使って書き直しなさい。

配列の中から必要なものだけを取り出して処理する

配列の中から必要なものだけを取り出して、処理する形はよくあるプログラムの「パターン」です。 このパターンに慣れておく必要があります。if文とうまく組み合わせることがこつです。

数の大小の判定などは、次の式でできます。
 
  
a == baとbは等しい
a != baとbは等しくない
a < baはbより小さい
a > baはbより大きい
a <= baはb以下
a >= baはb以上

また、一つの条件式で書けない複雑な条件式(真偽の値の合成)は、 論理演算子と呼ばれるものを使って、条件を組み合わせて表現します。

「かつ」(and)は && で表現され、「または」(or)は || で表現できます。 これらは条件と条件の間に書きます。 また、「でない」(否定, not)は ! で表し、条件の前に書きます。

 
  
A && BAかつB
A || BAまたはB
! A Aでない

例えば、「numが3以上でかつ10以下」という条件式は、次のようになります。

(num >= 3 && num <= 10)

また、「numが3または10」という条件式は、次のようになります。

(num == 3 || num == 10)

さらに、『「nが3以上で mが10以下」でない』という条件式は、次のようにかけます。

! (n >= 3 && m <= 10)

課題4-9

次の配列の中から、10以上、20以下の数をとり出し、表示するプログラムを作成しなさい。

numbers = [4,12,45,21,18,7,20,5,51,16,10]
#この後の部分を作る

出力は以下のようになる。

12
18
20
16
10

課題4-10

上の課題で、今度は10以上、20以下の数をとり出すのではなく、取り出した数だけ合計し結果を出力しなしなさい。
出力は以下のようになる。

10以上、20以下の数の合計は76です。

課題4-11

上の課題で、今度は10以上、20以下の数の合計ではなく、そのような数が幾つあったのかを数え、その数を出力しなしなさい。
出力は以下のようになる。

10以上、20以下の数は全部で5個あります。

課題4-12

以下のnekodataという変数の中の配列にはたくさんの「ねこ」が入っています。この中から、プログラムを作って、"とらねこ"を探し出して、何匹いるのか、その数を報告してください。以下のnekodataという変数に配列を代入する部分をプログラムの頭にコピーして使ってください。

nekodata = ["しろねこ", "みけねこ", "みけねこ", "しろねこ", "しろねこ", "みけねこ", 
 "しろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ",
 "みけねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "みけねこ",
 "しろねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ",
 "みけねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ", "しろねこ",
 "くろねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ",
 "しろねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ",
 "しろねこ", "くろねこ", "しろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ",
 "くろねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "しろねこ",
 "しろねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "みけねこ", "みけねこ",
 "しろねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "みけねこ",
 "しろねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "くろねこ", "しろねこ",
 "くろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ",
 "みけねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ",
 "くろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ",
 "しろねこ", "くろねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "みけねこ",
 "しろねこ", "くろねこ", "みけねこ", "みけねこ", "くろねこ", "みけねこ", "しろねこ",
 "みけねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "くろねこ",
 "みけねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ", "しろねこ",
 "しろねこ", "しろねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "くろねこ",
 "みけねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "くろねこ", "くろねこ",
 "くろねこ", "くろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "とらねこ",
 "しろねこ", "とらねこ", "とらねこ", "くろねこ", "みけねこ", "しろねこ", "とらねこ",
 "しろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "みけねこ", "とらねこ",
 "くろねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ",
 "しろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ",
 "みけねこ", "とらねこ", "みけねこ", "とらねこ", "みけねこ", "しろねこ", "しろねこ",
 "みけねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ",
 "とらねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ",
 "みけねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "くろねこ",
 "みけねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "くろねこ", "くろねこ",
 "みけねこ", "くろねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ",
 "くろねこ", "しろねこ", "ばけねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ",
 "しろねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "みけねこ", "くろねこ",
 "しろねこ", "くろねこ", "みけねこ", "しろねこ", "とらねこ", "しろねこ", "しろねこ",
 "みけねこ", "くろねこ", "うみねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ",
 "みけねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "しろねこ",
 "くろねこ", "みけねこ", "しろねこ", "みけねこ", "くろねこ", "みけねこ", "くろねこ",
 "みけねこ", "みけねこ", "くろねこ", "とらねこ", "くろねこ", "しろねこ", "しろねこ",
 "みけねこ", "しろねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ",
 "しろねこ", "みけねこ", "みけねこ", "しろねこ", "しろねこ", "しろねこ", "くろねこ",
 "くろねこ", "みけねこ", "しろねこ", "くろねこ", "しろねこ", "とらねこ", "しろねこ",
 "くろねこ", "みけねこ", "くろねこ", "うみねこ", "しろねこ", "くろねこ", "しろねこ",
 "みけねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ",
 "みけねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ",
 "みけねこ", "くろねこ", "みけねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ",
 "みけねこ", "くろねこ", "みけねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ",
 "しろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ",
 "しろねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "くろねこ", "みけねこ",
 "くろねこ", "くろねこ", "しろねこ", "みけねこ", "くろねこ", "しろねこ", "みけねこ",
 "くろねこ", "みけねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ",
 "くろねこ", "とらねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ",
 "くろねこ", "しろねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ",
 "みけねこ", "くろねこ", "しろねこ", "みけねこ", "やまねこ", "しろねこ", "しろねこ",
 "くろねこ", "しろねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ",
 "みけねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ", "しろねこ",
 "くろねこ", "みけねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ",
 "とらねこ", "とらねこ", "くろねこ", "しろねこ", "しろねこ", "みけねこ", "くろねこ",
 "しろねこ", "くろねこ", "くろねこ", "くろねこ", "みけねこ", "くろねこ", "しろねこ",
 "しろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ",
 "しろねこ", "しろねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ",
 "しろねこ", "くろねこ", "あかねこ", "くろねこ", "みけねこ", "みけねこ", "しろねこ",
 "みけねこ", "みけねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "くろねこ",
 "くろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ", "しろねこ"]

[ヒント:文字列の異同の判定]

変数の中の文字列が同じかどうかなども、数と同様に==を使って判定できます。

  if(neko == "くろねこ") 
    ...
  end

プログラムの中で文字列を扱うには、"..."(ダブルクオート)で囲むことを忘れずに。

繰り返しから抜ける

これまで、繰り返しが終わるのは、whileの(条件判定式)を実行し、その値がfalseになったときだけでした。
これだけだと何かと不便なので、if文とbreakを組み合わせることで、繰り返しから脱出することができるようになっています。
break文を実行すると、(直前の) whileの繰り返しから抜けて、次に進みます。
注意: 繰り返しの中に繰り返しがあるような「入れ子になった繰り返し」の場合、 break文によって抜けられるのはそれを含む一番内側の繰り返しです。
次のように使います。

while (条件判定式)
...
...
  if (条件判定式)
    break
  else
    ...
  end     # if 
...
end   # while 

[ヒント:無限に繰り返す]

回数を限定しないで繰り返すためには、

while (true)
  ...
  ...
  ...
end   # while 

というような形が使えます。条件判定式の中が常にtrue(真)なので、終わりません。
次のような形も使えます。while(true)の代わりに使えます。

loop do
  ...
  ...
  ...
end  # loop 

[ヒント:breakの使い方]

次のように、breakを使えばよい。

while (...)
  ...
  input = gets.to_i
  if (input == 0)
    break
  else
    print "... それではもう一回やりましょう。\n"
    sleep(2)
  end  # if 
  ...
end  # while 

課題4-13

課題4-12のnekodataの中に"あかねこ"があれば "みつけた!"と表示してbreakを使って繰り返しをぬけ、 なかった場合は"いなかった"と表示するプログラムを作成しなさい。
この結果はどうなるでしょう。

課題4-14

課題4-13と同様に、nekodataの中に今度は"ぶちねこ"があれば "みつけた!"と表示してbreakを使って繰り返しをぬけ、 なかった場合は"いなかった"と表示するプログラムを作成しなさい。
この結果はどうなるでしょう。

いくつかのプリント文:print、 p、 puts

Rubyにはprintの他にもディスプレイに文字を出力するための命令文がいくつか用意されています。putsprintに似ていますが、引数一つ一つを表示するたびに自動的に改行が行われることが違いです。ここではpについて詳しく説明します。

pというのは、主にプログラム開発中に用いるprint文です。つまり、プログラムを作成する過程で誤りをみつけたり、 途中の段階まで正しく動作していることを確かめたりするときに使います。 (完成したプログラムには使わないようにしてください。)
では実際にprintとどのような違いがあるのか、比較して見てみましょう。まずはprintの場合です。

print "123\n"
print 123
123
123

printを使うと、文字列の "123\n" も、数の 123 も、同じように 123 と表示されます。

今度はpを使った場合です。

p "123\n"
p 123
"123\n"
123

pを使うと、文字列は "123\n" 、数は 123 というように、プログラム中での表現と同じ形で出力されます。 改行コード("\n")も無効になり、そのままの形で出力されます。 実行結果が改行されているのは、pには自動的に改行を入れる働きがあるからです。

最後にpを使って配列を出力した結果を見てみましょう。

p [1, 2, 3]

ary = [4, 5, 6]
p ary
[1, 2, 3]
[4, 5, 6]

配列の場合も、やはりそのままの形で出力されます。変数に代入した場合も、printと同じように 変数から値を取り出し、取り出した値をそのままの形で出力してくれます。

このように、pには変数の持つ値をわかりやすい形で出力してくれる働きがあります。 プログラムが複雑になればなるほど、実行過程の変数の値を正確に把握するのが困難になります。 しかし、それがわからなければ正しいプログラムを書くことはできません。 そんなときはこのpを使って途中経過を確認しておくことが必要になります。

pを使って全角文字(2バイト文字)を表示するときは、 ruby ではなく ruby -Ks としてrubyを起動するか、 もしくはプログラムのファイルの1行目に $KCODE = "s" と書いておいてください。 これを書かないと、全角文字をpで表示したときに文字化けしてしまいます。

p "文字化けします。"
"\225\266\216\232\211\273\202\257\202\265\202\334\202\267\201B"
$KCODE = "s"

p "こちらは文字化けしません。"
"こちらは文字化けしません。"

実行時にKsというオプションを付けた場合は以下のとおりです。

H:>ruby -Ks program.rb

課題4の続き

ここでは配列を使った、今までよりも少し難しい問題を扱います。 この問題を解くことによって、どのようにプログラムを組んだらよいか、 問題を解くためにこのプログラムではどういうことをどういう順番でさせていったらよいか、つまり、アルゴリズム を考える必要性が分かることでしょう。

課題4-13

数を要素とする配列の中から最大の要素を表示するプログラムを作成しなさい。 例えば

data = [2,4,27,3,19,26,24,34,10,23,1,11,21,7,8,6,25,35,30,9] 
に対しては35が表示されます。

[ヒント:最大の要素を求めるアルゴリズム]

このプログラムには、配列を記憶する変数以外に、(途中までの)最大値を記憶するための変数が必要になります。それを仮に x とします。
まず最初に、xに配列の最初の要素をいれておきます。 そして、配列の要素をひとつずつxと比較し、それがxよりも大きければxの値をその値にします(更新する、という)。 配列の要素をすべて調べた後のxの値が求める最大の要素です。

課題4-14

課題4-13とは逆に、最小の要素を表示するプログラムを作成しなさい。 課題4-13のdataの値では、1が表示されます。

課題4-15

数を要素とする配列の中から2番目に大きな要素を表示するプログラムを作成しなさい。 例えば

data = [2,4,27,3,19,26,24,34,10,23,1,11,21,7,8,6,25,35,30,9] 
に対しては34が表示されます。ここで、dataの値が[1,2,3,3]のように最大値が2つある配列の場合は、その中で2番目に大きな数、この例では最大値でもある3を返すものとします。

[ヒント:2番目に大きな要素を求めるアルゴリズム]

課題4-13では(途中までの)最大値を記憶するための変数 x が必要でした。 この課題では、それ「以外」に、2番目に大きな要素を記憶する変数が必要です。 それを仮に y とします。 まず、配列の先頭と2番目の要素の大きい方をx、小さい方を yとします。 そして、それら以外の配列の要素をひとつずつxおよびyと比較し、xとyの値を更新します。(ここでどういう比較をどういう順番で行えば効率がよくなるか、よく考えてください)。 配列の要素をすべて調べた後のyの値が求める2番目大きな要素です。


オブジェクト

これまでに学んできたプログラミングでは、その対象として"今日は"のような文字列や、 1234のようなを主に扱ってきました。 さらに、文字列や数を格納できる配列が新たに登場しました。 このように、変数の中に格納することができ、命令、操作の対象となるものを一般に オブジェクトと呼びます。 これから、もっといろいろな種類のオブジェクトを扱っていくことで、 より実際的で効果的なプログラミングを可能にしていきます。 今後の学習を進めていく上で、オブジェクトという言葉を使えるようになることが必要です。

オブジェクトの種類

これまで扱ってきた文字列オブジェクトには String という正式名称があります。 英語で「文字列」の意味です。数の場合には、整数と小数点のついた数(浮動小数点数、といいます)が区別されていますが、それぞれFixnumとFloatというのが正式名称です。 ただ整数の中でも、非常に大きな数はBignumという呼び名がつけられています。 なお、配列の正式名称はArrayです。
場合によって、このような名前を取り出せると便利なことがあります。 次のような方法でオブジェクトからそのオブジェクトの名前を取り出すことができます。 それをプログラムの中で使う練習を後でやります。
このような、ドットつまり、"." を使って、オブジェクトを対象にして、 いくつか命令をつなぎ合わて作る命令の表現を、ドットによる命令表現という意味で、 ドットノーテーションと呼ぶことがあります。
obj = "みけねこ"
p obj.class.name
"String"

課題5-1

次のそれぞれのオブジェクトの正式名称(クラス名)を上の方法を使って調べて報告しなさい。
"みけねこ"
200
31.5
500000000000000000
[23,15,8]
"みけねこ"           => String
200                  => #ここを報告
31.5                 => #ここを報告
500000000000000000   => #ここを報告
[23,15,8]            => #ここを報告

課題5-2

オブジェクト.class.nameというドットノーテーションで結ばれた命令を使って、オブジェクトの種類を文字列として取り出すことができましたが、これを利用して次の問題を解決しなさい。
以下の配列には、文字列と数が混在している。配列の中から文字列だけを取り出し、出力するプログラムを作成しなさい。
aray = [1200, 23, "ばけねこ","こねこ","こねこ",11,10,"みけねこ",25,"うみねこ","こねこ"]
#この後の部分を作る
ここで、出力の形式は以下のようなものとします。
ばけねこ こねこ こねこ みけねこ うみねこ こねこ

ファイル

ここではファイルについて学びます。 ファイルは、現代のコンピュータシステムの基本要素の一つです。 ワードプロセッサーや表計算ソフトのように、どんなアプリケーションプログラムでもファイルを使います。

実際、実用的なプログラムを作る上で、ファイルをプログラムの中から扱かえるようにすることは不可欠です。 ファイルから情報を読み込んで処理したり、その結果をファイルの中に書き込んだりすることが必要になります。

実は、ファイルをプログラムで扱うこと自体はそれほど難しいことではありません。 以下で、プログラムの中からファイルを扱う方法を学びましょう。

ファイルへの書き出し

次のプログラムを実行すると、コマンドプロンプトのウインドウに名前を出力します。
ary = ["中京太郎", "豊田次郎", "八事花子"]

for name in ary
  print "名前:",name , "\n"
end  # for 
名前:中京太郎
名前:豊田次郎
名前:八事花子
この結果を、ファイルにとっておくためには、少し準備が必要です。 結果をとっておくファイル名を"names.txt"にすることにします。
まず、次の命令を実行して、ファイルを扱えるようにしなければなりません。
fo = open("names.txt", "w")

特定の名前のファイルを開いて(openして)、そのファイルを扱うための仕掛け(ファイルオブジェクトと呼びます)を、 適当な変数(上の場合はfo)に代入しておきます。 プログラムの中では、そのファイルオブジェクトに対して、書いたり読んだりする操作をしていきます。

openという命令の、ファイル名の次にある"w"は、ファイルを書き出し(Write)の形にするためのものです。(ここでその名前のファイルがある場合には、 その中身が消去されることに注意してください。 なお、中身を消去せずに「追加書き」するものや、 ファイルから読み込みするものは後で述べます。) ファイル名も書き出しの"w"も、どちらも文字列で表します。 二重引用符(ダブルクォート)を忘れないように気を付けましょう。

fo = open("ファイル名", "w")
これだけ準備しておけば、後は簡単です。
ary = ["中京太郎", "豊田次郎", "八事花子"]

fo = open("names.txt", "w")

for name in ary
  fo.print "名前:",name, "\n"
end # for 

fo.close
このプログラムを実行すると、names.txtというファイルが作られ、print文の出力が書き込まれます (既にファイルがある場合は上書きされます)。 print文の前に fo. と付け加えられていることに注意してください。 これまでのprint文と違い、この場合は、 変数foの中のファイルオブジェクトに対して書き出すという操作を行います。
プログラムの最後に付いているfo.closeという命令は、変数foの中のファイルオブジェクトに対し、ファイルへの書き込みが終わったので、 後始末の処理(ファイルの保存など)をするためものです。 この命令を書き忘れても当面は何も困ったことは起こりませんが(プログラム終了時に自動的にcloseされます)、習慣として書くようにしましょう。 言い換えれば、ファイルは一度開けたら(openしたら)、閉めて(closeして)おいたほうがよいのです。

課題6-1

上のファイルの書き出しのプログラムを実行して、実際にnames.txtというファイルが作成されて、中身がprint文の出力になっているかどうか、Terapadなどを使って確かめて報告しなさい。

課題6-2

上で、実行したファイルへの書き出しプログラムと、その前に例として出ているコマンドプロンプトwindowへ書き出すプログラムを比較し、どこが違うのか、違う点を箇条書きにして報告しなさい。

ファイルへの追加書き

先の課題で、names.txt があるとします。ここで、 ファイルを開く(openする) ときにopen命令で"w"の代わりに、"a"としてみましょう。
ary = ["名古屋政美", "愛知京子", "中部喜朗"]

fo = open("names.txt", "a")
for name in ary
  fo.print "名前:",name, "\n"
end # for 

fo.close
このプログラムを実行すると、すでにあるnames.txtというファイルの中身は 消されずに、その後ろに、print文の出力が書き込まれます。 これを「追加書き」(Append)といいます。

課題6-3

上のプログラムを実行し、names.txt ファイルの中身を確かめよ。

ファイルからの読み込み

今度は、プログラムでファイルの中身を読み込む方法を学びます。
ファイルを読み込むためには、読み込む対象となるファイルが存在している必要があります。 上で作ったnames.txtの中身を読み込むことにしましょう。 names.txtという名前のファイルが存在し、その中身が次のようなものであることをTerapadなどで確認しておいてください。

名前:中京太郎
名前:豊田次郎
名前:八事花子

まず、ファイルを扱えるようにしなければならないところは、ファイルの書き出しとほとんど同じです。 書き出しと同様、特定の名前のファイルを開いて(open)して、それを扱うための仕掛け(ファイルオブジェクト)を、 適当な変数(以下の場合はfo)に入れておき、それに対して、読む操作をしていきます。

fo = open("names.txt", "r")

ちなみに、ここではファイルから文字列の「読み込み」を行うので、 右辺の括弧の中の二つ目の文字列が読み込み(Read)を指定する"r"になっています。 (書き出しの場合は"w"でした。)

この準備をしておき、getsを使うと、ファイルの内容を1行だけ読み込みます。 これまでの、キイボードから文字列を入力する(読み込む)ときに使ったgetsと同じです。

$KCODE = "s"  #pを使って日本語を表示するときに必要です。

fo = open("names.txt", "r")

line1 = fo.gets  #1行目の読み込み。
line2 = fo.gets  #2行目の読み込み。
line3 = fo.gets  #3行目の読み込み。
line4 = fo.gets  #4行目の読み込み。

fo.close

p line1
p line2
p line3
p line4
これを実行した結果、以下のような表示が得られるでしょう:
"名前:中京太郎\n"
"名前:豊田次郎\n"
"名前:八事花子\n"
nil

getsで読み込まれた1行分の文字列には、改行文字("\n")もついていることに注意してください。
最後のnilというのは、「無」を表す値です。 names.txtには、4行目以降何も書かれていませんので、文字列ではなく、nilが表示れます。

次のプログラムはnames.txtのファイルの内容を、getsによって読み込んでは、すぐに出力することを繰り返し、ファイルの内容をすべて表示するプログラムです。

fo = open("names.txt", "r")

while(line = fo.gets)  #nilを読み込むまで繰り返す。
  print line
end  # while 

fo.close

ファイルからの読み込みは、whileの条件式のところで行っています。 実はwhileは、条件式の値がfalseかnilになるまで繰り返しを続けます。 この場合は、ファイルからの読み込みの値がnilになるまで、読み込みと出力を繰り返します。

課題6-4

getsなどを使って、課題6-1で作成した names.txtというファイルの内容を一行づつ読み込み、コマンドプロンプトのウインドウにそのまま出力するプログラムを作成しなさい。

getsではなくreadlinesを使ったファイルからの読み込み

ファイルは便利なものですから、それを扱うための命令もたくさんあります。 ファイルからの読み込みも種種ありますが、ここでは、readlinesという、ファイル全体を一度に読み込む命令を学んでおきましょう。
まず、次の命令を実行して、ファイルを扱えるようにしなければならないところは、 これまでの、ファイルの書き出し、読み込みと同じです。 特定の名前のファイルを開いて(open)して、それを扱うための仕掛け(ファイルオブジェクト)を 適当な変数、(以下の場合はfo)に入れておき、それに対して、読む操作をしていきます。 ちなみに、ここではファイルからの文字列の「読み込み」を行うので、右辺の 括弧の中の二つ目の文字列が読み込みを指定する"r"になっています。 (書き出しできるようにする場合は"w"でした。)

この準備をしておき、readlinesを使うと、ファイルの内容を全体として 配列の中に読み込めます。 内容は配列の中に、一行が一つの配列の要素として格納されます。 次のプログラムはnames.txtのファイルの内容をreadlinesを使って aryという配列の中に読み込み、その配列の内容をpによって、 見えるようにしたものです。

$KCODE = "s"
ary = []

fo = open("names.txt", "r")

ary = fo.readlines
p ary

fo.close

課題6-5

fo.readlinesの結果が返すものを詳しく検討して、getsが返すものと何が違うのか述べなさい。
文字列か配列かなど。もし配列であるならば、配列の要素は何で、要素の数は何に対応するのかなどもできるだけ詳しく述べること。

文字列の分割

ファイルの内容を読み込んだ場合などもそうですが、文字列を分割する必要がでてくることが少なくありません。
文字列を分割することで、その内容を処理することができるからです。
次のsplit文字列オブジェクトに対して実行すると、スペースのところで分割され、分割されたものを要素とする配列ができあがります。
str = "こねこ おやねこ こねこ こねこ うみねこ こねこ のらねこ みけねこ こねこ"
ary = str.split
p ary
実行結果は次のようになります。
["こねこ", "おやねこ", "こねこ", "こねこ", "うみねこ", "こねこ", "のらねこ", "みけねこ", "こねこ"]

課題6-6

上の結果を利用して、次のスペースで区切られた文字列の中に「こねこ」が何匹いるのか数えて出力するプログラムを作成しなさい。 表示の形式は指定された形にすること。
str = "こねこ おやねこ こねこ こねこ うみねこ こねこ のらねこ みけねこ こねこ"
#この後の部分を作る

出力の形は以下のようにすること。
「こねこ」の数は全部で5匹です。

課題6-7

Nekos.txtというファイルの中には、いろいろな「ねこ」が文字列として入っています。このファイルの内容を読み込み、そのまま書き出すプログラムをgetsを使って作成してください。(注意: 文字コードに注意。Nekos.txtはShift_JISで書かれている)
出力は次のようになる。
しろねこ みけねこ みけねこ しろねこ しろねこ みけねこ しろねこ しろねこ みけねこ
みけねこ しろねこ みけねこ しろねこ みけねこ みけねこ しろねこ
くろねこ くろねこ しろねこ
みけねこ みけねこ しろねこ くろねこ くろねこ しろねこ みけねこ
みけねこ しろねこ みけねこ くろねこ しろねこ みけねこ
しろねこ みけねこ
しろねこ ばけねこ
...
...省略
...
しろねこ しろねこ くろねこ しろねこ
くろねこ しろねこ
くろねこ しろねこ しろねこ
しろねこ しろねこ しろねこ みけねこ みけねこ しろねこ くろねこ
しろねこ
くろねこ みけねこ みけねこ
しろねこ みけねこ みけねこ しろねこ くろねこ しろねこ くろねこ くろねこ
くろねこ みけねこ しろねこ みけねこ みけねこ みけねこ

課題6-8

課題6-6と課題6-7で作ったプログラムを元にして、「とらねこ」が何匹いるのかを数えるプログラムを作成しなさい。

[ヒント1]

課題6-7では、ファイルの内容を1行ずつ読み込むプログラムを作成しました。 読み込んだ行は、次のような文字列として扱うことができます。

#1行目
"しろねこ みけねこ みけねこ しろねこ しろねこ みけねこ しろねこ しろねこ みけねこ"

#2行目
"みけねこ しろねこ みけねこ しろねこ みけねこ みけねこ しろねこ"

#3行目
...

課題6-7では読み込んだ文字列をそのまま出力していましたが、今度は「とらねこ」の数を数えなければいけません。 ねこの数を数えるのは、課題6-6のプログラムが参考になりそうですね。

[ヒント2]

ファイルから1行読み込むたびに、「とらねこ」の数を数え、合計に加算していく必要があります。

合計は最初は0匹。
while...  #ファイルから1行ずつ読み込む。

  「とらねこ」の数を数える。

  合計に「とらねこ」の数を加算する。
end   # while 

「とらねこ」の数を数える所は、課題6-4で作成した通りですので、おそらく次のような形になるでしょう。

  str = 1行分の文字列
  ねこの文字列を分割する。
  「とらねこ」は最初0匹。
  while...  #ねこを1匹ずつチェックする。(for文を使ってもよい。)
    「とらねこ」なら1匹加算する。
  end   # while 

このプログラムを、上の合計を数えるプログラムに組み込む必要がありますので、完成すると次のような形になるはずです。

合計は最初は0匹。
while...  #ファイルから1行ずつ読み込む。

  str = 1行分の文字列
  ねこの文字列を分割する。
  「とらねこ」は最初0匹。
  while...  #ねこを1匹ずつチェックする。(for文を使ってもよい。)
    「とらねこ」なら1加算する。
  end  # while 

  合計に「とらねこ」の数を加算する。
end  # while 

ファイルから1行ずつ読み込む繰り返しの中で、ねこを1匹ずつチェックする繰り返しを行います。 (while文の中にwhile文が入ります。)


関数

関数とは何か 1

関数によって、役に立つプログラムに名前をつけ、 その名前を使によりプログラムを呼び出して利用することができるようになります。 これがプログラミングの中での関数働きの基本です。

関数の定義は次のように、プログラムの頭、defとendで囲んで書かれます。

def 関数名
  本体プログラム
end

入力を受け取る関数

プログラムに名前をつけるだけではなく、次のように、入力を取れるようにすることで、関数はもっと強力になります。

def 関数名(引数) #関数の入力に対応する変数のことを仮引数(かりひきすう)と呼びます。
  プログラム本体
end

関数名(引数)     #関数の呼び出し部分です。この入力に対応する値のことを
                  #実引数(じつひきすう)と呼びます。

課題7-1

次のプログラムを実行すると、何が表示されるのか予想し、その表示結果を答えなさい。 次に、実際に実行してみて、予想結果と合うかどうかを確かめ、その結果も報告しなさい。 予想と違った場合には、なぜ違ったのか、理由を説明しなさい。

def aisatsuHiruma(name)
  print "こんにちは、", name, "さん。"
  print "\n"
end  # def 

def aisatsuYoru(name)
  print "こんばんは、", name, "さん。"
  print "\n"
end  # def 

name = "太郎"
aisatsuHiruma(name)

name = "花子"
aisatsuHiruma(name)

name = "太郎"
aisatsuYoru(name)

name = "花子"
aisatsuYoru(name)

課題7-2

次のプログラムを実行すると、何が表示されるのか予想し、その表示結果を答えなさい。 次に、実際に実行してみて、予想結果と合うかどうかを確かめ、その結果も報告しなさい。 予想と違った場合には、なぜ違ったのか、理由を説明しなさい。

def aisatsuHiruma(name)
  print "こんにちは、",name,"さん。"
  print "\n"
end   # def 

names = ["名古屋", "愛知", "中部", "大阪"]

for name in names
  aisatsuHiruma(name)
end

複数の値を受け取る関数

関数は二つ以上の引数を受け取ることもできます。 複数の値を受け取る関数を定義する場合、次のように仮引数の変数名をカンマで区切って指定します。

def 関数名(変数1, 変数2, ... , 変数n)
  プログラム
end

この関数を呼び出して使用するときは、定義部分と同じように、実引数の値(もしくは変数名)をカンマで区切って指定する必要があります。

関数名(値1, 値2, ... , 値n)

一つ目の引数(変数1、値1)を第一引数と呼び、二つ目の引数(変数2、値2)を第二引数と呼びます。 関数が実行されると、第一引数の変数1には値1が代入され、第二引数の変数2には値2が代入されます。

出力を返す関数

以下は、1からn(この場合は100)までの総和を求めるプログラムです。

n = 100
sum = 0
for i in 1..n
  sum = sum + i
end    # for 
print "合計は", sum, "です。\n"

n=100 と n=1000 と n=10000 の3つの数について和を求めるには、次のように、これを繰り返し使うことでできます。

n = 100
sum = 0
for i in 1..n
  sum = sum + i
end    # for 
print "合計は", sum, "です。\n"

n = 1000
sum = 0
for i in 1..n
  sum = sum + i
end    # for 
print "合計は", sum, "です。\n"

n = 10000
sum = 0
for i in 1..n
  sum = sum + i
end    # for 
print "合計は", sum, "です。\n"

このプログラムもやはり、足し算の部分を sowa というような名前の関数にし て使えるようにすればよいのですが、さらに次のように 入力出力 を取れるようにすると、とても強力になります。

def sowa(num)
  sum = 0
  for i in 1..num
    sum = sum + i
  end     # for 
  return(sum)
end   # def 

n = 100
print "合計は", sowa(n), "です。\n"

n = 1000
print "合計は", sowa(n), "です。\n"

n = 10000
print "合計は", sowa(n), "です。\n"

入力と出力を取る関数は、次のような形で、作ることができます。 入力に対応する変数のことを引数と呼びます。 出力される値のことを戻り値とか返り値 と呼ぶこともあります。

def 関数名(引数)  #入力に対応する値を、関数を呼び出したところから受け取ります。
  プログラム本体
  return(値)      #出力に対応する値を、関数を呼び出したところに返します。
end

このように関数を定義しておくと、関数名(引数) をプログラムの中に 書くだけで、上の sowa(num) のように、簡単に繰り返し使うことができるよう になります。 このように、プログラムの中に定義した関数の名前を書い てやると、そこで関数が実行され、結果が値として得られることになります。

課題7-4

次の関数を使ったプログラムが、どのような出力を出すか、 プログラムを一歩一歩追いかけることで予測しなさい。

def nijiKansu(m)
  kekka = m * m + 3 * m + 2
  return(kekka)
end   # def 

n = 1
print "入力値が", n, "のとき、出力値は", nijiKansu(n), "です。\n"

n = 2
print "入力値が", n, "のとき、出力値は", nijiKansu(n), "です。\n"

n = 3
print "入力値が", n, "のとき、出力値は", nijiKansu(n), "です。\n"

課題7-5

次のプログラムはn=5のとき、 数nに対して階乗(n x (n-1) x ... x 3 x 2 x 1)を計算するものです。 これをヒントに、まず、関数を使わずに、n=5, n=10, n=50の時の階乗を計算し、 次々に結果を出力するプログラムを作成しなさい。

n = 5
kekka = 1
for i in 1..n
  kekka = kekka * i
end   # for 
print "答え= ",kekka,"\n"
なお、出力結果は次のようになります: [50!の表示]
Cのようなプログラミング言語だと、50の階乗の計算はできても、結果を整数として表示することは普通できません。しかし、Rubyなら(何も特殊なことをせずとも)このようなことができてしまいます。 これもRubyの魅力の一つです。
答え= 120
答え= 3628800
答え= 30414093201713378043612608166064768844377641568960512000000000000

課題7-6

次に、数nに対して階乗(n x (n-1) x ... x 3 x 2 x 1)を計算する関数、 kaijo(n)を作成し、それを使って、課題7-5と同様に、 n=5, n=10, n=50の時の階乗を計算し、値を出力するプログラムを作成しなさい。

課題7-7

数を要素とする配列を入力に取り、その総和を計算し、 値を返す関数arraySum(ar)を作成しなさい。 またこれを使って、以下のようにいろいろな配列の総和を出力するプログラムを作成しなさい。

ar = [3,5,2,7]
p arraySum(ar)
ar = [12,24,63,11,29]
p arraySum(ar)
ar = [1251,3567,9399,6241]
p arraySum(ar)
なお、出力結果の例は次です:
17       # ar = [3,5,2,7] 
139      # ar = [12,24,63,11,29] 
20458    # ar = [1251,3567,9399,6241]  

関数の中の変数名の有効範囲(スコープ)

変数名には、変数名が意味を持つ有効範囲があります。 特に、関数の内部で使われた変数名はその関数の内部でしか意味を持ちません。 その結果、定義された関数の内部と外部で同じ名前の変数を使用しても、別の変数として扱われる。ため、 同じ変数名が使われていてもお互いの間で値のやりとりは起こりません。 このような変数名の有効範囲のことを、変数のスコープ(scope)とも言います

具体的な例で見てみましょう。下の例で、関数nijouの中の変数answerの値は変化しますが、その変化は関数の外のanswerの値に何の影響も与えません。

def nijou(num)
  answer = num * num
  print "関数の中のanswerの値は", answer, "\n"
  return(answer)
end   # def 

answer = 0
print "5の二乗は", nijou(5), "\n"    #関数nijouを呼び出して実行し、戻り値を表示する。
print "答えは", answer, "です。\n"
この実行結果は以下のとおりです:
関数の中のanswerの値は25
5の二乗は25
答えは0です。

このように、関数の中の変数が外の変数に影響を持たないことで、関数を作る際に、他のところで使われている変数からの余計な影響を気にせずにつくれるようになります。 このことは、特に、大きなプログラムを分担して複数の人で作成するような場合に、重要になります。

このことから、関数の中で得られた結果は、変数を使って受け渡すことはできず、returnを使って、その関数の戻り値として受け渡すことになります。

def nijou(num)
  answer = num * num
  print "関数の中のanswerの値は", answer, "\n"
  return(answer)     #変数answerの値を戻り値として出力する。 
end   # def 

answer = nijou(5)    #関数nijouを呼び出して実行し、戻り値を代入する。
print "答えは", answer, "です。\n"
この実行結果は以下のとおりです:
関数の中のanswerの値は25
答えは25です。

課題8-1

次の文字列の中にに何匹「しろねこ」がいるのか数えて、その数を返すプログラムをshironeko_count(str)という関数をまず作成し、それを使って数えなさい。

def shironeko_count(str)
  ...#以下を作る。
  ...
end  # def 

str = "しろねこ みけねこ しろねこ くろねこ しろねこ みけねこ くろねこ しろねこ みけねこ"

print "しろねこは", shironeko_count(str), "匹います。\n"

結果は次のように表示しなさい。

しろねこは4匹います。

課題8-2

今度は、文字列とねこの種類を渡すと、そのねこの数を文字列の中から数えるneko_count(str,type)という関数を作り、それを使って、しろねこの数を数えるプログラムを作成しなさい。

def neko_count(str, type)
  ...#以下を作る。
  ...
end  # def 

str = "しろねこ みけねこ しろねこ くろねこ しろねこ みけねこ くろねこ しろねこ みけねこ"
neko = "しろねこ"

print neko, "は", neko_count(str, neko), "匹います。\n"

課題8-3

課題8-2で作った関数neko_count(str,type)を使って、次の文字列の中から、「しろねこ」だけでなく、「くろねこ」と「みけねこ」の数も数えるプログラムを作成しなさい。

str = "しろねこ みけねこ しろねこ くろねこ しろねこ みけねこ くろねこ しろねこ みけねこ"
しろねこは4匹います。
くろねこは2匹います。
みけねこは3匹います。

課題8-4

次のように要素が数、または配列からなる配列がある。

ary = [3, 51, 2, [22, 7, 4, 8], 3, 5, [12, 13], 98]

このような配列を引数にとり、すべての数の合計を計算するプログラムを作りなさい。

この実行結果は以下のとおりです:

228

[課題8-4のヒント]

君たちの中には、

 [3, 51, 2, [22, 7, 4, 8], 3, 5, [12, 13], 98]
において、配列の中のカッコ([ ]) を外してしまえば、
 [3, 51, 2, 22, 7, 4, 8, 3, 5, 12, 13, 98]
となるから、前に作ったarraySumが使えるのでは、と思った人がいるかもしれない。

しかし、この方法は使えない。 もしも入力が "[3, 51, 2, [22, 7, 4, 8], 3, 5, [12, 13], 98]" というような「文字列」なら、文字としてのカッコを消す方法はある。 しかしこの問題の場合、カッコは「文字」ではなく「配列がそこにあることを示すマーク」 として表示されたものに過ぎない(言い換えれば、計算機の中では、文字列として表現されていない)。 だから、このような方法は使えないのである。
ここではプログラミングとしての「正攻法」について解説する。

基本は、課題7-7で作った arraySumである。 ただarraySumは要素が数の場合だけを想定し、その和を求めるものであった。
ここでは、要素が数か配列かを分けて考える必要がある。 なお、配列の要素が配列か数かは、次のように.class.name (または .class だけでもできる...)を使って判定できる。

ary = [3, 1]
p ary.class.name
num = 234
p num.class.name

結果は

"Array"
"Fixnum"

となる。

なお、要素が配列の場合、arraySumを使えば、その配列の要素の和が得られることは言うまでもないだろう。