前のページ 次のページ 目次

4. メソッド

4.1 +-は演算子ですか

+-などは演算子ではなくメソッド呼び出しです.したがって オーバーライドすることもできます.

class MyString < String
  def +(other)
    print super(other)
  end
end
ただし,以下のものは制御構造であり,オーバーライドできません.

=, .., ..., !, not, &&, and, |, or, !=, ~
単項演算子をオーバーライドするには,メソッド名として+@-@を 使います.

4.2 関数はありますか

厳密に言えばRubyに関数はありません.Rubyにおける関数(みたいなもの)はす べてレシーバを省略した形のメソッドです.例えば

def writeln(str)
  print(str, "\n")
end

writeln("Hello, World!")
のように一見関数のように見えるものも,mainというオブジェクトのメ ソッドです.したがってRubyを純粋なオブジェクト指向言語と呼ぶことができ るわけです.

でも使う時はメソッドであることを意識する必要はありません.

4.3 オブジェクトのインスタンス変数を参照できますか

インスタンス変数を参照するメソッドが定義されていれば,そのメソッドを 使って値を知ったり,値をセットしたりすることができます. Module#attrattr_readerattr_writerattr_accessor を参照してください. もちろん,自分でメソッドを定義して参照することもできます.

method=の形のメソッド定義は,method=の間に空白をいれることが できませんが,メソッド呼出しの時には,空白をいれることが可能ですし, +,-などのメソッドが定義されていれば,+=,-=などの自己代入も行えます.

Object#instance_evalを使えば,直接参照することもできます.

4.4 privateprotectedの違いが分かりません

privateの意味は,メソッドを関数形式でだけ呼び出せるようにし, レシーバー形式では呼び出せないようにするという意味です.したがって, 可視性がprivateなメソッドは,自クラス及びサブクラスからしか参照 できません.

protectedも同様に,自クラス及びサブクラスからしか参照できませんが, 関数形式でもレシーバー形式でも呼び出せます.

メソッドのカプセル化に必要な機能です.

4.5 インスタンス変数をpublicにしたいのですが

できません.

インスタンス変数をpublicにすることはデータのカプセル化という観点から見 てあまり好ましくありません.インスタンス変数へのアクセスはアクセスメソッ ドを介して行われます.Rubyではattrメソッドを使うことで外部に は変数のように振舞わせることが可能です.

class Foo
  def initialize(str)
    @name = str
  end
  attr("name")
  # This means:
  # def name
  #   return @name
  # end
end

foo = Foo.new("Tom")
print foo.name, "\n"         # Tom
attr(name, public)で省略可能な二番目の引数にTRUEを指定するこ とで書き込みメソッドを提供することも可能です.

class Foo
  def initialize(str)
    @name = str
  end
  attr("name", true)  # これはこういうことです.
  # def name
  #   return @name
  # end
  # def name=(str)
  #   @name = str
  # end
end

foo = Foo.new("Tom")
foo.name = "Jim"
print foo.name, "\n"    # Jim
Module#attr_reader, attr_writer, attr_accessorも 参照してください.

4.6 メソッドの可視性を指定したいのですが

最初に断わっておくと,Rubyでは関数形式(レシーバを省略した形)でしか呼び 出すことのできないメソッドのことをprivateなメソッドと呼びます.ちょっ と変ってますね.

クラスのメソッドをprivateにすれば外部から呼び出すことができなくなりま す(ただしそのクラスのサブクラスからは呼び出すことができます).クラス 内でしか呼び出すことのないメソッドはprivateにしておくとよいでしょう.

次のようにすればメソッドをprivateにすることができます.

class Foo
  def test
    print "hello\n"
  end
  private :test
end

foo = Foo.new
foo.test
# -> test.rb:9: private method `test' called for #<Foo:0x400f3eec>(Foo)
クラスメソッドをprivateにするにはprivate_class_methodを使います.

class Foo
  def Foo.test
    print "hello\n"
  end
  private_class_method :test
end

Foo.test
# -> test.rb:8: private method `test' called for Foo(Class)
同様にpublicpublic_class_methodを用いることでメソッドを publicにすることができます.

デフォルトでは,クラス内でのメソッド定義はinitializeを除いてpublic, トップレベルではprivateになっています.

4.7 メソッドの引数の*は何ですか

Cウィザードのみなさん,これはポインタではありません.Rubyでは引数に *を付けることで,不定個の引数を配列に格納した形で受け取ることがで きます.

def foo(*all)
  for e in all
    print e, " "
  end
end

foo(1, 2, 3)
# -> 1 2 3
またメソッド呼び出しで*を付けた配列を渡すと配列を展開して渡すこと ができます.

a = [1, 2, 3]
foo(*a)
現在,*をつけることができるのは

  1. 多重代入の左辺
  2. 多重代入の右辺
  3. 引数リスト(定義側)
  4. 引数リスト(呼び出し側)
  5. caseのwhen節

の末尾だけです.(1)は

x, *y = [7, 8, 9]
のような形式で,この場合 x = 7,y = [8, 9] になり ます.

x, = [7, 8, 9]
のような記述もでき,この場合,x = 7 で

x = [7, 8, 9]
なら,x = [7, 8, 9] になります.

4.8 仮引数にデフォルト値を指定できますか

できます.

しかもこのデフォルト値は関数の呼び出し時に評価されます.Rubyのデフォル ト値は任意の式が可能で(C++はコンパイル時に決まる定数のみ),評価はメソッ ドのスコープで呼び出し時に行われます.

4.9 引数は参照渡しですか

参照渡しです.したがって,参照されているオブジェクトが,自分の 状態を変更するメソッドを持っている時には,副作用(それが主作用かも しれませんが)に注意する必要があります.

値渡しでなくて参照渡しなのは,関数呼び出しのたびにオブジェクトがコピー されるのを防ぐためです.これは,Cで配列や文字列がポインタで渡ること,構造体を ポインタで渡すことが多いことと同じです.また,Smalltalkやlispの実装は Rubyと全く同じです.

4.10 メソッド名に大文字で始まる識別子は使えますか

使えます.ただし,メソッド呼出しの時に引数を括る()を省略できません.

4.11 superArgumentErrorになりますが

メソッド定義中でsuperと呼び出すと,引数がすべて渡されますので, 引数の数が合わないとArgumentErrorになります.異なる数の引数を 指定するには,super()に引数を指定してやります.

4.12 2段階上の同名のメソッドを呼びたいのですが

superは,1段上の同名のメソッドを呼び出します.それより上の同名の メソッドを呼び出すには,あらかじめそのメソッドをaliasしておきます.

4.13 組込み関数を再定義した時に,元の関数を呼びたい時はどうしますか

メソッド定義の中ではsuperが使えます.再定義する前にaliasしておくと, 元の定義が保たれます.Kernelの特異メソッドとしても呼べます.

4.14 破壊的メソッドとは何ですか

オブジェクトの内容を変更してしまうメソッドで,文字列や配列,ハッシュ などにあります.同名のメソッドがあって,一方はオブジェクトのコピーを 作って返し,もう一方は変更されたオブジェクトを返すようになっている場合, !のついた方が破壊的メソッドです.

4.15 a +bがエラーになりますが

a(+b)と解析されています.+の両側の空白をなくすか,いれるか のどちらかにしてください.

4.16 s = "x"; puts s *10 がエラーになりますが

puts s *10 のところが,s(*10)というメソッド呼出しと解析されて しまいます.s*10にするか,s * 10にしてください.

4.17 p {}で何も表示されません

{}がハッシュのコンストラクタではなく,ブロックと解析されています. p({})としてください.

4.18 def pos= (val) print @pos,"\n"; @pos = val endと定義しても,pos = 1で参照できません

=のついたメソッドは,レシーバー形式で呼ぶ必要があります. self.pos = 1 という形で呼んでください.

4.19 副作用が起こるのはどんな時ですか

実引数となったオブジェクトに対して,メソッドの中から破壊的メソッドを 適用した場合です.

def foo(str)
  str.sub!(/foo/, "baz")
end

obj = "foo"
foo(obj)
print obj
# -> "baz"
この場合,引数となったオブジェクトが変更されています.でも,これは,プログラム の中で必要があって副作用のあるメッセージをオブジェクトに対し て送っているので当たり前です.

4.20 複数の戻り値を戻すことはできますか

できません.

でもご心配なく,Rubyでは

return 1, 2, 3
とすると配列が返されます.つまり,

return [1, 2, 3]
とするのと同じです.

さらに多重代入を利用すると,複数の戻り値を戻すのとほとんど同じことがで きます.たとえば,

def foo
  return 20, 4, 17
end

a, b, c = foo
print "a:", a, "\n" # -> a:20
print "b:", b, "\n" # -> b:4
print "c:", c, "\n" # -> c:17
こんなことができるわけです.


前のページ 次のページ 目次