【2018年版】Macで学ぶ、Rubyの入門講座 中級編

こんにちは、サブローです。

Rubyを勉強したいけど、どのようにして学べばいいかわからない、という方へ。

Macを使って、プログラミング言語であるRubyを学んでいこう、ということで、前回は、基礎編としてやってきましたが、今回は中級編ということで、Rubyをより深くを解説します。

この記事を書いている僕は、情報系の大学を修了し、プログラミングにおいては、ある程度学びましたが、Rubyに関しては、趣味程度でしか触っていないので、前回と同様、今回も、たのしいRuby 第5版をもとに、解説をしていきます。

目次

オブジェクト
変数
多重代入
条件判断
繰り返し
timesメソッド
for文
while文
until文
eachメソッド
loop
メソッドの分類
メソッドの定義
クラスとは
継承
クラスを定義してみよう
アクセスメソッド
特別な変数self
クラスメソッド

オブジェクト

Rubyでは、データを表現するための基本的な単位をオブジェクトといいます。

今まで出てきたものだと、数値オブジェクト、文字列オブジェクト、配列・ハッシュオブジェクト、正規表現オブジェクトなどがあります。

クラス

クラスとは、オブジェクトの種類を表すもので、オブジェクトの性質は、属するクラスによって決まります。

これまでに出てきたオブジェクトのクラスは次の通りです。

オブジェクト クラス
数値 Numeric
文字列 String
配列 Array
ハッシュ Hash
正規表現 Regexp
ファイル File
シンボル Symbol

変数

変数とは、オブジェクトに付ける名札のようなもので、それぞれのオブジェクトを識別するために使います。

Rubyには、次の4つの変数があります。

  • ローカル変数
  • 先頭はアルファベットの小文字か「_」

  • グローバル変数
  • 先頭は「$」

  • インスタンス変数
  • 先頭は「@」

  • クラス変数
  • 先頭は「@@」

この他、「nil」、「true」、「false」、「self」などは、疑似変数と呼べれ、値を変更することはできません。

定数

定数は、変数と同様に名札の働きをしますが、1度代入したあとでもう1度代入をすると、警告が出されます。

そのため、定数はプログラム上で何度も参照される、変更をすることがない値に名前をつけるときに使います。

定数は、アルファベットの大文字で始まります。

予約語

変数では、ユーザが任意の文字列を使うことができますが、その際、システムで使用が制限されている文字列を「予約語」といいます。

うっかり、「end」や「next」といった文字列を変数として使うと、構文エラーとなります。

多重代入

これまで、「変数 = 値」の形式で、代入を行ってきましたが、複数の変数への代入を1つの式で行うことができます。

これを「多重代入」といいます。

    a = 1
    b = 2
    c = 3

という代入は、次のように書くことができます。

    a , b , c = 1 , 2 , 3

多重代入は、プログラムをわかりにくくする場合があるので、まとめるのは関係のある変数のみにしなければなりません。

次のように、変数に1つだけ「*」をつけると、余った値はまとめて、配列に代入されます。

    a , b , c* = 1 , 2 , 3 , 4 , 5

条件判断

初級編では、少ししか触れていませんが、ここでは、条件判断について、詳しく解説します。

条件判断とは

まずは、入力された年の単位を西暦から平成に変換するプログラムを例としましょう。

#西暦から平成に変換

ad = ARGV[0].to_i
heisei = ad - 1988
puts "平成#{heisei}年です。"

コマンドラインから入力された文字列を整数に変換し、そこから1988を引いた値を表示しています。

実行すると、

Ruby conditional1

となります。

ただ、このプログラムには問題があるのが、お分かりでしょうか。

そうです。

1988より小さい値を入力すると、正しい表示とは言えなくなります。

たとえば、このようにマイナスの値が返ってきます。

Ruby conditional2

このような場合、「変換ができません」と表示させるべきでしょう。

このように、

「ある条件のときには○○という処理を、そうでないときには☓☓という処理をさせたい」

といったときに使うのが条件判断文です。

条件判断文には、大きく分けて次の3つがあります。

  • if文
  • unless文
  • case文

論理演算子

それぞれの条件判断文を解説する前に、論理演算子「&&」「||」について解説します。

「&&」と「||」は、複数の条件を1つにまとめるときに使います。

条件1 && 条件2

は、条件1と条件2がどちらも「真」であった場合に、全体が「真」になります。

一方、

条件1 || 条件2

は、どちらか一方が「真」であれば、全体も「真」になります。

また、否定の論理演算子

!条件

は、条件を反転させます。つまり、条件が真の場合には「偽」になり、条件が偽の場合には「真」になります。

if文

では、if文をみていきましょう。

if文は、もっとも基本的な条件判断文で、次のように書きます。

if 条件 then
    処理
end

#thenは省略可能です。

さらに、elsigとelseを加えた場合の書き方は次のようになります。

if 条件1 then
    処理1
elsif 条件2 then
    処理2
elsif 条件3 then
    処理3
else
    処理4
end

#thenは省略可能です。

条件は上から判定され、条件1から3までがすべて偽の場合のみ、文4が実行されます。

サンプルプログラムをみてみましょう。

a = 10
b = 20

if a > b
    puts "aはbよりも大きい"
elsif a < b
    puts "aはbよりも小さい"
else
    puts "aとbは同じ"
end

これは単純に、aとbを比較していて、3通りの場合分けをif、elsif、elseで表現しています。

unless文

if文と反対の動きをするのが、unless文で、次のように書きます。

unless 条件 then
    処理1
else
    処理2
end

#thenは省略可能です。

unless文では、条件が「偽」の場合に、処理が実行されます。

if文の書き方と一緒ですね。ただ、unless文に「elsif」のような書き方はありません。

unless文は、否定の演算子「!」を使って書き換えることができる上に、そもそも、意味が分かりにくくなるので、あまり使わない方がいいかもです。

case文

条件が多く、比較したいオブジェクトが1つの場合は、case文を使った方がシンプルに書けます。

case文は次のように書きます。

case 比較したいオブジェクト
when 値1 then
    処理1
when 値2 then
    処理2
when 値3 then
    処理3
else
    処理4
end

#thenは省略可能です。

比較したいオブジェクトが値と一致した場合に、処理が実行されます。

また、whenはいくつでも増やすことができ、複数の値を指定することもできます。

では、サンプルをみてみましょう。

array = ["a" , 1 , nil]

array.each do |item|
    case item
    when String
        puts "item is a String."
    when Numeric
        puts "item is a Numeric."
    else
        puts "item is something."
    end
end

#thenは省略可能です。

実行結果はこのようになります。

Ruby conditional3

このサンプルでは、与えられたオブジェクトが、文字列か数値かどれでもないかを判断して、結果を表示しています。

if修飾子とunless修飾子

if文とunless文は、次のように書くこともできます。

puts "aはbよりも大きい" if a > b

if文が式の後ろにきていますね。

この場合は、よりシンプルになるので、何を実行させたいのかを目立たせたいときに使えます。

繰り返し

条件判断と並んで、繰り返しもプログラミングでは欠かすことのできない要素です。プログラムを書いていると「同じ処理を繰り返したい」ということが多々あります。

例えば、画面に複数の線を表示したいとか、配列の中身を全て表示したい、とか。

こういった時に、同じ文を何度も書くのはめんどうですよね。

そういった同じ文を1つの文で書けるのが、「繰り返し」という仕組みです。

Rubyにおける、繰り返しの実現する方法は、次の6つです。

  • timesメソッド
  • for文
  • while文
  • untilメソッド
  • eachメソッド
  • loopメソッド

1回目では、while文までを解説していきます。

timesメソッド

繰り返しの回数が決まっている場合は、次のようにtimesメソッドを使います。

回数.times do
    繰り返したい処理
end

ブロックの部分は「do 〜 end」ではなく、「{ 〜 }」を使うこともできます。

回数.times {
    繰り返したい処理
}

また、

回数.times do |i|
    繰り返したい処理
end

のように、ブロックに変数を与えてあげると、繰り返しの回数を取得できます。

では、サンプルプログラムをみてみましょう。

5.times do |i|
    puts "#{i}回目の繰り返し"
end

実行結果は、こうなります。

Ruby repeat1 1

実行結果の通り、繰り返しの初期値は「0」となり、初期値を「1」にすることはできません。

ブロックの中で「#{i + 1}」として、表示を変更することはできますが、プログラムのわかりやすさの観点からすると、あまり好ましくはありません。

繰り返しの回数を知る必要がない場合はtimesメソッドを使い、知る必要がある場合は、次に説明するfor文やwhile文を使います。

for文

for文も、処理を繰り返すために使われます。ただ、for文はメソッドではないため、そういう書き方があるんだ、と覚えてください。

for文は、次のように書きます。

for 変数 in オブジェクト do
    繰り返したい処理
end

#doは省略可能

このオブジェクトには、どのようなオブジェクトでも指定できるわけではなく、範囲オブジェクト、配列オブジェクト、ハッシュオブジェクトなどを指定できます。

サンプルプログラムをみてみましょう。

names = ["satoh" , "suzuki" , "takahashi" , "tanaka"]

for name in names
    puts name
end

配列の要素を1つずつ取り出して、それぞれを表示する、という処理を繰り返しています。

これは、範囲オブジェクトを使って書き換えることもできます。

names = ["satoh" , "suzuki" , "takahashi" , "tanaka"]

for i in 0..3
    puts names[i]
end

実行結果は、どちらも変わらず、次のようになります。

Ruby repeat1 2

while文

while文は、どのような繰り返しでも使えるように、単純な構文になっています。

while 条件 do
    繰り返したい処理
end

#doは省略可能

while文では、条件が成り立っている間、ずっと、処理が繰り返されます。

サンプルプログラムを用いて、プログラムの動き方をみていきましょう。

i = 1

while i < 3
    puts i
    i += 1
end

実行結果です。

Ruby repeat1 3

iは初期値1を持っていて、まずは、1が表示されています。

その後、iの値は2となり、2が表示されます。

iの値が3になった段階で、whileの条件を満たさなくなったため、3は表示されずに、プログラムが終了します。

では、for文との違いについて、考えてみましょう。

結論から言うと、繰り返しの回数にあります。

for文では、範囲オブジェクトを使って「1..5」のように回数を指定しますが、while文では回数を指定するのではなく、条件で指定をします。

そのため、while文では、「i += 1」のように繰り返しの回数をブロックの中で指定し、条件に合わなくなるまでという形式で回数が決まります。

一方、for文では、ブロックを用いて回数を管理する必要はなく、「1..5」などとすれば、勝手に1が足されて、次の処理に移ります。

そのため、繰り返しの回数を条件にする場合は、while文ではなく、for文を使うべきでしょう。

until文

if文に対してunless文があったように、while文に対してもuntil文がありあます。

until文は、条件の判定が逆になり、条件を満たしていない間、繰り返しが行われます。

until文は次のように書きます。

until 条件 do
    繰り返したい処理
end

#doは省略可能

while文と形は同じですね。では、サンプルプログラムをみてみましょう。

sum = 0
i = 1

until sum >= 50
    sum += i
    i += 1
end

puts sum

sumが50以上にならない間、つまり、50以上になると繰り返しが終わります。

また、このプログラムは否定の演算子を使って、whileで書き換えることができます。

sum = 0
i = 1

while !(sum >= 50)
    sum += i
    i += 1
end

puts sum

このように、until文は、whileと「!」を使って、書き換えることができるので、使わなくてもプログラムを書くことはできます。

ただ、条件式が複雑で、その否定を考えるのが直感的でない場合は、until文を使いましょう。

eachメソッド

以前、配列やハッシュのところで出てきましたが、eachメソッドは、配列のような、オブジェクトの集まりに対して、それを1つずつ取り出すときに使います。

eachメソッドは、次のように書きます。また、timesメソッドと同じように、ブロックには「{ 〜 }」を使うことができます。

オブジェクト.each do |変数|
    繰り返したい処理
end

オブジェクト.each {|変数|
    繰り返したい処理
}

これらは、次の処理とほぼ同じ動きをします。

for 変数 in オブジェクト
    繰り返したい処理
end

for文は特殊なもの、と説明をしていましたが、それは、for文がRubyの内部処理としては、eachメソッドを用いて実行されているためです。

なので、eachメソッドが呼び出せるオブジェクトは、for文のinのあとに指定することができます。

loop

終了条件のない、単なる繰り返しのためのメソッドもあります。

それが、loopメソッドで、次のように書きます。

loop do
    繰り返したい処理
end

終了条件がないって、結構怖いですよね。

たとえば、loopメソッドの中に、「puts "Ruby"」とでも書こうものなら、ひたすら画面上にRubyの文字が表示され続けることになります。

このような事態にならないように、次の章で繰り返しの制御を解説します。

繰り返しの制御

繰り返しの途中で、処理を中断させたり、処理を次に飛ばしたい、ということがあります。

そのようなときには、次のような命令を使います。

命令 用途
break 繰り返しを中断し、繰り返しから抜ける
next 次の回の繰り返しに処理を移す
redo 同じ条件で繰り返しをやり直す

redoに関しては、ほとんど使われることがないため、この記事では、breakとnextについてのみ、解説します。

では、サンプルプログラムをみてみましょう。

puts "breakの例"

i = 0

["satoh" , "suzuki" , "takahashi" , "tanaka"].each do |name|
    i += 1
    if i == 3
        break
    end
    p [i , name]
end

puts "nextの例"

i = 0

["satoh" , "suzuki" , "takahashi" , "tanaka"].each do |name|
    i += 1
    if i == 3
        next
    end
    p [i , name]
end

実行結果は、このようになります。

Ruby repeat2 1

では、breakの動き方を解説します。

breakは、繰り返し全体を中断します。つまり、その時点で繰り返しが終了します。

したがって、iが3になった時点でbreakが呼ばれ、インデックスが3の「takahashi」以降は、表示されません。

次に、nextの動き方をみてみましょう。

nextが呼ばれた時点で、その回の処理は終了し、次の回の処理に移行します。

そのため、インデックスが3の「takahashi」のみが表示されず、他の3つは表示される、ということになります。

メソッド

メソッドは、オブジェクトを操るための操作についての記述です。

オブジェクトに定義されているもので、全ての操作はメソッドとして実装されています。

まずは、メソッドの呼び出しをおさらいしていきましょう。

単純なメソッド呼び出し

メソッドのもっとも単純な呼び出しは次のように書きます。

オブジェクト.メソッド名(引数1, 引数2, …,引数n)

オブジェクトの後に「 . 」が置かれ、そのあとにメソッド名を指定します。メソッド名の後の「()」で、引数を与えます。

引数の数と順番は、メソッドごとに決められているので、それに従って指定しなければなりません。

ちなみに、この「()」は省略可能です。

また、オブジェクト指向の考え方の中において、オブジェクトはレシーバ(receiver)と呼ばれます。

これは、メソッドを実行するということを「オブジェクトにメッセージを送る」、その結果として「オブジェクトはメッセージを受け取る(receiveする)」と考えるからです。

実行するメソッドのレシーバはどのオブジェクトなのか、ということは意識しておくほうがよいでしょう。

ブロック付きメソッド呼び出し

eachメソッドやloopメソッドのように、ブロックを伴って呼び出されるメソッドがあります。

このようなメソッドをブロック付きメソッド呼び出しといい、次のように書きます。

オブジェクト.メソッド名(引数, …) do |変数, …|
    ブロックの内容
end

「do 〜 end」の部分がブロックで、ブロックは「{ 〜 }」で書くこともできます。

オブジェクト.メソッド名(引数, …) {|変数, …|
    ブロックの内容
}

ブロックの頭にくる変数をブロック変数といい、ブロックを実行するときに、メソッドからパラメータが渡されます。

演算子の形式のメソッド呼び出し

四則演算などの2項演算子、-(マイナス)などの単項演算子、配列で出てくる「[]」も、演算子の形をしたメソッドです。

例としては次のようなものがあり、

  • obj + arg
  • -obj
  • obj[arg]

それぞれ、「obj」がレシーバ、「arg」が引数となっている立派なメソッドです。

メソッドの分類

メソッドの呼び出しをおさらいしたところで、次に、メソッドの種類についてみていきましょう。

メソッドには次の3種類があります。

  • インスタンスメソッド
  • クラスメソッド
  • 関数的メソッド

ここでは、この3種類のメソッドについて解説していきます。

インスタンスメソッド

インスタンスメソッドは、もっとも一般的なメソッドで、あるオブジェクトがあったときに、そのオブジェクトをレシーバとするメソッドのことをいいます。

インスタンスメソッドには、次のようなものがあります。

p "10 , 20 , 30 , 40".split(" , ")
p [1 , 2 , 3 , 4].index(2)
p "100".to_s

実行してみましょう。

Ruby method1 1

上から順に文字列、配列、数値の各オブジェクトがレシーバになっていて、どのインスタンスメソッドが使えるかは、オブジェクトのクラスによって決まっています。

クラスメソッド

レシーバが、インスタンスではなく、クラスだった場合、そのメソッドはクラスメソッドと呼ばれます。

たとえば、新しいインスタンスを作る場合が、そうですね。

Arraw.new
File.open("some_file")
Time.new

インスタンスを操作しないけれども、そのクラスの関連メソッドを使いたいときもクラスメソッドが使われます。たとえば、ファイルクラスのrenameメソッドとか。

File.rename(oldname, newname)

さらに、演算子の形をしたクラスメソッドもあります。

Array["a", "b", "c"]

関数的メソッド

レシーバがないメソッドを関数的メソッドと呼びます。

レシーバに該当するオブジェクトが本当にないわけではないのですが、関数的メソッドの場合は、それが省略されています。

また、関数的メソッドは、レシーバの状態によって結果がかわることがないように作られています。

たとえば、次のようなものがあります。

print "Hell, World."
sleep(10)

メソッドの定義

前回は、メソッドとはどういうものなのか、を解説しました。

今回は、あらかじめ用意されているメソッドを使うのではなく、自身でメソッドを定義する方法を解説していきます。

メソッドを定義するための一般的な構文は、次にようになります。

def メソッド名(引数1, 引数2, …)
    実行したい処理
end

メソッド名には、アルファベット、数字、アンダーバーを使うことができますが、数字ではじめてはいけません。

では、サンプルプログラムをみてみましょう。

def hello(name)
    puts "Hello, #{name}."
end

hello("Ruby")

実行結果です。

Ruby method2 1

メソッドに「"Ruby"」の文字列が渡され、表示されているのが分かると思います。

また、メソッドに渡す引数には、デフォルトの値を渡すことができ、次のように書きます。

def hello(name="Ruby")
    puts "Hello, #{name}."
end

hello()
hello("Newbie")

実行結果です。

Ruby method2 2

上のプログラムのように、「引数=値」でデフォルトの値を指定でき、引数が空の場合は、デフォルトの「"Ruby"」が表示されているということが分かるかと思います。

メソッドの戻り値

次のようにreturnを使うことで、メソッドの戻り値を指定できます。

return 値

例として、直方体の体積を求めるメソッドを作成してみます。

def volume(x, y, z)
    return x * y * z
end

p volume(2, 3, 4)
p volume(10, 20, 30)

実行結果です。

Ruby method2 3

returnは省略可能で、returnを省略した場合は、メソッドの中で最後に得られる値が戻り値となります。

条件によってメソッドを終了させたい場合など、return文を明示的に書く必要がないときは、省略する方がよいでしょう。

ブロック付きメソッドの定義

ブロック付きメソッドの定義の例として、与えられたブロックを繰り返し実行するloopメソッドと同じ動きをする、myloopを定義してみましょう。

def myloop
    while true
        yield
    end
end

num = 1
myloop do
    puts "num is #{num}"
    break if num > 10
    num *= 2
end

実行結果です。

Ruby method2 4

3行目において、「yield」という命令が出てきました。これが、ブロック付きメソッドを定義する上でもっとも重要なものになるので、必ず覚えるようにしましょう。

メソッドに与えられたブロックは、「yield」において実行されます。

whileにtrueを与えた場合、そのままだとループを抜けることはできませんが、yieldの部分で、breakが実行さるため、16を表示した段階でループが終了します。

引数の数が不定なメソッド

メソッドは、引数の数を決めなくても定義することができ、「*変数名」とすることで、全てをまとめて配列で受け取ることができます。

def foo(*args)
    args
end

p foo(1, 2, 3)

実行結果です。

Ruby method2 5

引数が、配列になって返ってきているということが分かるかと思います。

クラスとは

これまでは、どのプログラミング言語においても備わっている、データ型やメソッドについて書いてきました。

ここからが本題ということで、いよいよオブジェクト指向についての解説をしていきます。

まずは、クラスについてなんですが、クラスとは、オブジェクトの種類を表すものです。

Rubyのオブジェクトは、例外なく、何らかのクラスに属しています。

これまでに出てきた、配列オブジェクトは、Arrayクラスのオブジェクト(インスタンス)ですし、単に文字列と呼んでいたオブジェクトも、実際は、Stringクラスのオブジェクト(インスタンス)です。

同じクラスのオブジェクトは、同じメソッドを受け付けるので、クラスとは、雛形や設計図と言われたりもします。

では、クラスの生成方法をみていきましょう。

クラスは、newメソッドを使って生成します。

ary = Array.new
p ary

実行してみると、

Ruby class1 1

このように、空の配列が生成されているのが、分かるかと思います。

また、オブジェクトがどのクラスに属しているかを知るには、classメソッドを使い、あるオブジェクトが、あるクラスのインスタンスかどうかを知るためには、instance_of?メソッドを使います。

ary = []
str = "Ruby"

p ary.class
p str.class
p ary.instance_of?(Array)
p ary.instance_of?(String)

実行結果です。

Ruby class1 2

継承

続いては、継承についてなんですが、これもオブジェクト指向プログラミングにおいて、重要な概念です。

ただ、意味が分かりづらく、使いどころを理解するのが難しいものなので、じっくりと考えるようにしましょう。

継承とは、すでにあるクラスに機能を追加して、新しいクラスを作ることをいいます。

継承を理解するために、アナログ時計とデジタル時計について考えてみましょう。

どちらも、時間の表示のしかたが違うだけで、現在時刻の取得方法やアラーム機能といった、基本的な機能は同じです。

こういった場合に、アナログ時計クラスとデジタル時計クラスを別々に定義するのではなく、時計クラスを定義して、それぞれに継承すればいいよね。

という考え方が、継承です。

また、継承を使うと、次にようなことができます。

  • 既存の機能はそのままで、新しい機能を追加できる
  • 既存の機能を書き換えて、同名のメソッドに違う振る舞いをさせる
  • 既存の機能に処理を追加して、メソッドを拡張する

なので、継承は、同じような機能を持った複数のクラスを作る場合に便利な概念です。

また、継承をして新しく作られたクラスをサブクラスといい、継承のもとになったクラスをスーパークラスといいます。

この2つのキーワードも重要なキーワードなので、覚えておくようにしましょう。

クラスを定義してみよう

では、クラスを定義してみましょう。

例として、HelloWorldクラスを定義してみます。

class HelloWorld
    def initialize(myname = "Ruby")
        @name = myname
    end

    def hello
        puts "Hello, world. I am #{@name}"
    end
end

bob = HelloWorld.new("Bob")
alice = HelloWorld.new("Alice")
ruby = HelloWorld.new

bob.hello
alice.hello
ruby.hello

実行結果です。

Ruby class1 3

では、このクラスをもとに、クラスの書き方についての解説をしていきます。

クラスの定義は次のように書きます。

class クラス名
    クラスの機能
end

このとき、クラス名は大文字で始めなければなりません。

続いて、initializeメソッドについてですが、このインスタンスメソッドによって、インスタンス変数を初期化しています。

上の例をみると、newメソッドに引数"Bob"を渡した場合、"Bob"がインスタンス変数@nameに代入され、引数がない場合は、初期値の"Ruby"のままとなります。

では、ここで出てきた、インスタンス変数とインスタンスメソッドとは、どのようなものなのかについて解説していきます。

インスタンスメソッドは、生成されたインスタンスから呼び出すことができるメソッドで、なにも難しくはないです。

ただ、インスタンス変数は少し理解が難しいので、詳しくみていきます。

上のinitializeメソッドの部分をもう一度みてみましょう。

def initialize(myname = "Ruby")
    @name = myname
end

@name = mynameの部分で、引数として与えられたオブジェクトをインスタンス変数@nameに代入しています。

この@nameのように、頭に「@」を付けて定義するのが、インスタンス変数です。

ローカル変数だと、同じメソッドの中だけでしか使えませんが、インスタンス変数の場合は、同じインスタンスの中であればメソッドを超えて使うことができます。

このため、引数のオブジェクトを保持したまま、helloメソッドを呼ぶことができるのです。

また、インスタンス変数は、インスタンスごとに違う値を持つことができるので、

bob = HelloWorld.new("Bob")
alice = HelloWorld.new("Alice")
ruby = HelloWorld.new

とした場合、実行結果をみると、インスタンス変数はインスタンスごとに違う値を持っていることが、わかるかと思います。

アクセスメソッド

では、続きからです。

まずは、前回のサンプルコードをもう一度みてみましょう。

class HelloWorld
    def initialize(myname = "Ruby")
        @name = myname
    end

    def hello
        puts "Hello, world. I am #{@name}"
    end
end

bob = HelloWorld.new("Bob")
alice = HelloWorld.new("Alice")
ruby = HelloWorld.new

bob.hello
alice.hello
ruby.hello

ここでは、インスタンス変数について考えてもらいたいのですが、このままだと、インスタンス変数@nameは、newメソッドで与えられた値、もしくは、初期値"Ruby"しか持つことができません。

なぜなら、Rubyでは、オブジェクトの外部からインスタンス変数を参照したり、代入することができないからです。

なので、オブジェクトの内部情報にアクセスするためには、アクセスをするためのメソッドを定義する必要があります。

このメソッドを「アクセスメソッド」と呼びます。

では、HelloWorldクラスにアクセスメソッドを追加してみましょう。

class HelloWorld
    .
    .

    def name
        @name
    end

    def name=(value)
        @name = value
    end

    .
    .
end

クラスにアクセスメソッドを追加したら、次のように使ってみましょう。

p bob.name

とすると、"Bob"と表示され、

bob.name = "Robert"
p bob.name

とすると、"Robert"と表示されます。

これで、インスタンス変数にアクセスできるようになりました。

ただ、アクセスメソッドを書くのって、少しめんどうですね。なので、Rubyではアクセスメソッドを簡単に書けるようになっています。

インスタンス変数を示すシンボルを指定すると、同名のアクセスメソッドを自動で定義してくれます。

class HelloWorld
    .
    .

    attr_accessor :name

    .
    .
end

このように、上の2つのメソッドは、1行で書けてしまいます。

特別な変数self

次は、インスタンスメソッドの中で、別のインスタンスメソッドを呼ぶような場合を考えてみましょう。

そういった場合、メソッドのレシーバ自身を参照するために、selfを使います。

selfなので、そのままの意味ですね。インスタンスの中で、インスタンスが自分自身を参照したいときに使います。

ちなみに、下記、クラスメソッドの定義においては、selfは、自身のクラスをレシーバとします。

では、HelloWorldクラスに次のコードを追加してみましょう。

class HelloWorld
    attr_accessor :name
    .
    .
    def greet
        puts "Hi, I am {self.name}"
    end

    def change_name
        name = "Ruby"
        self.name = "Ruby"
    end
    .
    .

greetメソッドでは、self.nameとして、アクセスメソッドnameを呼んでいるので、問題なくコンソールに表示されるかと思います。

Rubyでは、暗黙の了解として、インスタンメソッドとその中のインスタンスメソッドのレシーバは同じになるので、この場合、selfは省略可能です。

ただ、代入演算子=が入るアクセスメソッドname=に関しては、変数への代入が優先されるので、明示的にselfを書く必要があります。

なので、change_nameメソッドでは、name=とするとnameというローカル変数が定義されてしまうので、self.nameとしなければなりません。

クラスメソッド

ここまでは、インスタンスをレシーバとする、インスタンスメソッドについてを解説してきました。

ここからは、クラスそのものをレシーバとする、クラスメソッドについて解説していきます。

クラスメソッドは、その名の通り、インスタンスに対する操作ではなく、クラスに関連する操作のために使います。

クラスメソッドは、「class << クラス名 〜 end」という特殊なクラス定義の中に、インスタンスメソッドと同じように記述します。

class << HelloWorld
    def hello(name)
        puts "#{name} said hello."
    end
end

HelloWorld.hello("John")

とすると、「John said hello.」と表示されるかと思います。

この書き方の場合、クラスの外に書くことになるので、可読性が下がってしまいます。

なので、一般的には次のようにselfを使って、クラスの中に書きます。

class HelloWorld
    class << self
        def hello(name)
            puts "#{name} said hell."
        end
    end
end

HelloWorld.hello("John")

また、クラスメソッドは、「def クラス名.メソッド名 〜 end」と書くこともできるので、基本的には、次のように書くのが望ましいでしょう。

class HelloWorld
    def self.hello(name)
        puts "#{name} said hell."
    end
end

HelloWorld.hello("John")

続きは随時更新します。

SNSでもご購読できます。

コメントを残す

*