[目次] [第3回] [第5回]

Ruby勉強会(第4回)

練習3-1の回答例

練習3-1の回答例のプログラムの全貌は はex3-1a.rb〜ex3-1c.rbとしておいておく。

1.のプログラムについては、説明の通り4行目を「line = STDIN.readline」 を「line = readline」に置き換えるだけである。

2.は最後の行(11行目)を置き換える。ex3-1b.rbの11行目はあくまでも サンプルで、表示する書式自体は異なっても良い。

3.のプログラム例はex3-1c.rbである。ここでは例外は「ファイルの終り(EOF)」 のみと仮定している。そのため、例外が生じればループを脱出して、最後の計算 と表示をすれば良いので、begin〜rescueでループのすぐ外側を囲む。 こうすれば例外が生じるとループの外側に実行の制御が移るので、ループを 脱出することになる。そして、ここでは念のためにどんな例外が起きたのかを 画面に表示するために例外を表すオブジェクトを変数eに受け取る。 STDERRとは「標準エラー出力」のオブジェクトであり、エラー メッセージの表示などはこのオブジェクトに依頼するのが通例である。ここでは、 文字列の表示(改行つき)メソッドであるputsによって例外オブジェクト が内部に持っているメッセージを表示するようにしている。表示を終えると そのまま逐次処理としてendの次に移るので、結果を計算・表示 することになる。

サブルーチン

今回はサブルーチンとしてのメソッドを定義してみる。本来、メソッドは クラス定義の一部として定義するが、Rubyではクラス定義とは別にメソッドを 定義し、単純なサブルーチン、あるいは関数として使用することができる。

そういうメソッドは次のような形式で定義する。

def メソッド名 [(引数1, 引数2, ...)]
    メソッドで実行すべき文
    ...
end

この定義をそのメソッドを使用する文より前に記述する。引数が必要ない 場合は丸括弧ごと省略する。そのメソッドで値を返したい場合は、その中で 「return 式」という文を記述する。そのreturn文を 実行するとそのメソッドの呼び出し元に戻り、「」の値を 返り値とする。例えば、次の例は単に受け取った引数の値に1を加えて返す 単純なメソッドである。

def add_one (n)
    return n + 1
end

練習4-1

次のプログラム例は、生年月日をキーボードから入力すると、実行日まで 何日間生きてきたかを計算して表示するものである。

     1  print("Year? ")
     2  yr = readline
     3  print("Month? ")
     4  mn = readline
     5  print("Day? ")
     6  dy = readline
     7  birth = Time.local(yr, mn, dy)
     8  printf("Birth Date is %s.\n", birth)
     9  now = Time.now
    10  printf("Today is %s.\n", now)
    11  d = ((now - birth) / (60 * 60 * 24)).floor
    12  printf("You are living for %d days.\n", d)

メソッドprintは改行せずに画面に表示する。 Timeは Rubyにおいて日時を表すオブジェクトのクラスである。このクラスの localというメソッドを呼び出すと、引数で指定した年月日(必要なら 時分秒も指定できる)を表すオブジェクトを生成し、返す。ここではそれを birthという変数に格納している。このオブジェクトを文字列のつもりで 表示すると、「Sun Feb 03 00:00:00 JST 1980」のような形式で 表示される(時分秒を指定しない場合は00:00:00となる)。 nowという メソッドは現在時刻(日時)を表すオブジェクトを生成する。このTimeクラスの オブジェクトは加減算が可能であり、差は秒で表現される。そのため、11行目 では秒から日に変換するための計算を行い、メソッドfloorによって、 小数以下を切り捨てている。

  1. このプログラム例を実行してみよ(自分や知人の生年月日の他、 別に生年月日でなくても日数計算は可能である)。
  2. プログラム例の1〜6行目はメッセージを入力促進(プロンプト)として 表示し、キーから入力された1行を受け取るという同じパターンの処理である。 これをメソッドprompt_readとして定義し、1〜6行目を次のように 変えてみよ。
    def prompt_read (message)
        ここに必要な文を入れる
    end
    
    yr = prompt_read("Year? ")
    mn = prompt_read("Month? ")
    dy = prompt_read("Day? ")
    以下略
    

再帰呼び出し

あるメソッドを定義している際に、そのメソッド自身を呼び出しても良い。 そのようなメソッドの呼び出し方を「再帰呼び出し」と言う。例えば、 階乗計算の定義は次のようにできる。

これをRubyで定義したものが次のメソッドfactである。

def fact (n)
    if n == 0
        return 1
    else
        return n * fact(n - 1)
    end
end

ここで、2番目の定義の「n > 0」の比較を行っていないが、nが0ではないと いう意味でも良いと考え、elseで代用している(厳密には ifの 条件を<=とする方が良いかもしれない)。

練習4-2

上のメソッドfactにこれを呼び出す文を追加して、実行してみよ。 なお、Rubyでは整数はメモリが使える限り大きな数まで扱えるので、例えば、 100とか1000の階乗でも計算結果を正確に得ることができる。

クラス定義とメソッド

メソッドは本来各オブジェクトがどう振る舞うのかの手続きを記述する ものであるので、上記の用い方はRuby固有の特殊な使い方である。 本来メソッドはオブジェクトの型紙であるクラスの定義の一環として 定義する必要がある。Rubyにおける最も単純なクラス定義は以下の ように記述する。

class クラス名 [< スーパクラス名]
    メソッド定義
    ...
end

クラス名は大文字で始める。スーパクラスについては次回以降に説明 する。では、ここで「人」を表すオブジェクトのためのクラス「 Human」 を定義してみる。「人」は様々な要素から構成されるが、ここではまず「人は 生まれた時に名前が付けられる」という仮定の元にクラス定義を行う。

「名前」は文字列で表現するとして、それを持たせるためには オブジェクトの構成要素を入れておくための「インスタンス変数」が必要に なる。Rubyではインスタンス変数は変数名の先頭に「@」を1個つける 約束になっている。特に宣言文は必要なく、そのクラスのメソッド内で 使用されると自動的にそのクラスのインスタンス変数として組み入れられる。

新たなオブジェクトを生成するにはクラスに対して「new」という メソッドを呼び出すのが基本である。newには必要に応じて引数を 与えることができる。ここで、Humanクラスのインスタンスを生成、 つまり生み出す際に名前を引数として与えることにする。例えば、
Human.new("Tetsuo Sakaguchi")
のようにしてインスタンスを生成することにする。newメソッドが 呼ばれるとそのクラスは新たなインスタンスを生成し、そのインスタンスに 対してinitializeメソッドを呼び出して、インスタンスに初期化を 指令する。その引数はnewに与えられた引数である。そこで、 Humanには次のようなメソッドを持たせると良いことになる。

def initialize (name)
    @name = name
end

ここで、「@name」はインスタンス変数である。さて、これで生成の 際の処理は可能になったが、このままではそのインスタンスが持っている名前を 外部から知ることができない(デバッグなどのための機構を除く)。 そこで、外部から「名前は何ですか?」と聞かれて答えるのに相当する メソッドを追加する。ここではそのメソッドの名前を「name」とする。

def name
    return @name
end

単に名前を返すだけなので、引数は不要である。

練習4-3

ここまでの例を組み合わせ、必要な記述を補ってクラスHumanの 定義を完成させよ。そして、それに以下のような文を追加して、動作を 確認すること。

a = Human.new(適当な名前を指定する)
p(a.name)

それができれば、そのインスタンス(オブジェクト)そのものをメソッド p で表示してみよ。


[目次] [第3回] [第5回]

by Tetsuo Sakaguchi
$Date: 2008/01/25 06:55:43 $(UTC)