- クラス/メソッドの定義:
- 定義に関する操作:
クラス定義
class Foo < Super
def test
# ...
end
# ...
end
文法:
class 識別子 [`<' superclass ] 式.. end
文法:
class 識別子 [`<' superclass ] 式.. [rescue [error_type,..] [=> evar] [then] 式..].. [else 式..] [ensure 式..] end
クラスを定義します。クラス名はアルファベットの大文字で始まる識別子です。
rescue/ensure 節を指定し、例外処理ができます。例外処理については制御構造/begin参照。
クラス定義は、識別子で指定した定数へのクラスの代入になります (Ruby では、クラスもオブジェクトの一つで Classクラスのインスタンスです)。
クラスが既に定義されているとき、さらに同じクラス名でクラス定義を書くとクラスの定義の追加になります。ただし、元のクラスと異なるスーパークラスを指定すると TypeError が発生します。
class Foo < Array
def foo
end
end
# 定義を追加(スーパークラス Array を明示的に指定しても同じ)
class Foo
def bar
end
end
# 間違ったスーパークラスを指定するとエラー
class Foo < String
end
# => superclass mismatch for class Foo (TypeError)
クラス定義式の中は self がそのクラスであることと、 呼び出し制限のデフォルトが異なること以外にトップレベルとの違いはありません。クラス定義式中には任意の式を書くことができクラス定義の際に実行されます。
クラス定義はネスト(入れ子)にして定義できます。以下の例で入れ子の外側のクラス Foo と内側のクラス Bar の間には、定数 Bar が Foo の中の定数 Foo::Bar であること以外、継承関係などの機能的な関連はまったくありません。
class Foo
class Bar
end
end
クラス Foo が既に定義されていれば、以下の書き方もできます。
class Foo
end
class Foo::Bar
end
クラスのネストは、意味的に関連するクラスを外側のクラス/モジュールでひとまとまりにしたり、包含関係を表すために使用されます。
# 関連するクラスを Net というカテゴリにまとめる
# このような場合は外側は普通モジュールが利用される
# (Net のインスタンスがない。Net を include できるなどのため)
module Net
class HTTP
end
class FTP
end
end
obj = Net::HTTP.new
# あるいは
include Net
obj = HTTP.new
# 以下のような使い方は組み込みのクラスにも見られる
# 利用者は File::Constants を include することで、
# File::RDONLY などと書かずに直接 RDONLY と書くことができる。
class File
module Constants
RDONLY = 0
WRONLY = 1
end
include Constants
end
File.open("foo", File::RDONLY)
# あるいは
include File::Constants
File.open("foo", RDONLY)
# 上記はあくまでも例である。実際の File.open ではより簡便な
# File.open("foo", "r") という形式が使われる
クラス定義式は、最後に評価した式の結果を返します。最後に評価した式が値を返さない場合は nil を返します。
特異クラス定義
obj = Object.new # obj = nil でも可
class << obj
def test
# ...
end
# ...
end
文法:
class `<<' expr 式.. end
文法:
class `<<' expr 式.. [rescue [error_type,..] [=> evar] [then] 式..].. [else 式..] [ensure 式..] end
クラス定義と同じ構文で特定のオブジェクトにメソッドやインスタンス変数を定義/追加します。この構文の内部で定義したメソッドや定数は指定したオブジェクトに対してだけ有効になります。 Object#clone で生成したオブジェクトには引き継がれますが, Object#dup で生成したオブジェクトには引き継がれません.
rescue/ensure 節を指定し、例外処理ができます。例外処理については制御構造/begin参照。
特異クラス定義式は、最後に評価した式の結果を返します。最後に評価した式が値を返さない場合は nil を返します。
モジュール定義
module Foo
def test
# ...
end
# ...
end
文法:
module 識別子 式.. end
文法:
module 識別子 式.. [rescue [error_type,..] [=> evar] [then] 式..].. [else 式..] [ensure 式..] end
モジュールを定義します。モジュール名はアルファベットの大文字で始まる識別子です。
rescue/ensure 節を指定し、例外処理ができます。例外処理については制御構造/begin参照。
モジュール定義は、識別子で指定した定数へのモジュールの代入になります。 Ruby では、モジュールもオブジェクトの一つで Module クラスのインスタンスです。モジュールが既に定義されいるとき、さらに同じモジュール名でモジュール定義を書くとモジュールの定義の追加になります。
モジュール定義式は、最後に評価した式の結果を返します。最後に評価した式が値を返さない場合は nil を返します。
メソッド定義
def fact(n)
if n == 1 then
1
else
n * fact(n-1)
end
end
文法:
def メソッド名 ['(' [arg0 ['=' default0]] ... [',' '*' rest_args [, post ...]] [',' key1: [val1]] ... [',' '**'kwrest] [',' '&' block_arg]`)'] 式.. (body) [rescue [error_type,..] [=> evar] [then] 式..].. [else 式..] [ensure 式..] end
この定義のある場所にメソッドを定義します。すなわち、クラス/モジュール定義中ならばそのクラス/モジュールのメソッドを定義します。トップレベルならばどこからでも呼べるメソッドを定義します。このようなメソッドは結果として他の言語における「関数」のように使えます。
def hello # 引数のないメソッド。
puts "Hello, world!"
end
def foo(a, b) # 引数のあるメソッド。括弧を省いてdef foo a, bとも
a + 3 * b
end
メソッド名としては通常の識別子の他に、再定義可能な演算子(例: ==, +, - など 演算子式 を参照)も指定できます(演算子式の定義参照)。
class Vector2D
attr_accessor :x, :y # インスタンス変数@x, @yに対応するゲッタとセッタを定義
def initialize(x, y) # コンストラクタ
@x = x; @y = y # @がつくのがインスタンス変数(メンバ変数)
end
def ==(other_vec) # いわゆる演算子オーバーライド
other_vec.x == @x && other_vec.y == @y
end
def +(other_vec)
Vector2D.new(other_vec.x + @x, other_vec.y + @y)
end
# ...
end
vec0 = Vector2D.new(10, 20); vec1 = Vector2D.new(20, 30)
p vec0 + vec1 == Vector2D.new(30, 50) #=> true
仮引数にデフォルト式が与えられた場合、メソッド呼び出しで実引数を省略したときのデフォルト値になります。
def foo(x, y = 1) # 2番目の引数yにデフォルト値を指定
10 * x + y
end
p foo(1, 5) #=> 15
p foo(3) #=> 31
p foo #=> ArgumentError (wrong number of arguments)
デフォルト値を指定する引数の位置は、1つの連続した区間になっている必要があります。(デフォルト値を指定する2つの引数の間に、デフォルト値を指定しない引数があってはいけません)
def foo(a, b = 1, c = 2, d = 3, e)
[a, b, c, d, e]
end
p foo(10, 20) #=> [10, 1, 2, 3, 20]
p foo(10, 20, 30) #=> [10, 20, 2, 3, 30]
p foo(10, 20, 30, 40) #=> [10, 20, 30, 3, 40]
p foo(10, 20, 30, 40, 50) #=> [10, 20, 30, 40, 50]
# NG: 次の定義は SyntaxError になる
# def foo(x = 1, y, z = 2)
# end
デフォルト式の評価は呼び出し時にメソッド定義内のコンテキストで行われます。
$gvar = 3
def bar(x, y = $gvar) # 確かに定義時には$gvar == 3だが
10 * x + y
end
$gvar = 7
# 呼び出し時の$gvarの値が使われる
p bar(5) #=> 57 (!= 53)
仮引数の直前に * がある場合には残りの実引数 (後述の post 引数を除く) はみな配列とし てこの引数に格納されます。可変長引数、rest 引数などと呼ばれる機能です。このような引数は 1 つしか作れません。
def foo(x, *xs)
puts "#{x} : #{xs.inspect}" # Object#inspect は p のような詳細な内部表示
end
foo(1) #=> 1 : []
foo(1, 2) #=> 1 : [2]
foo(1, 2, 3) #=> 1 : [2, 3]
def bar(x, *) # 残りの引数を単に無視したいとき
puts "#{x}"
end
bar(1) #=> 1
bar(1, 2) #=> 1
bar(1, 2, 3) #=> 1
Ruby 1.9 以降では可変長引数よりも後にまだ通常の引数を置くことができます。
最後の仮引数の直前に & があるとこのメソッドに与えられているブロックが手続きオブジェクト(Proc)としてこの引数に格納されます。これは、イテレータを定義する方法の一つです。イテレータを定義する代表的な方法は yield を呼び出すことです。他に Proc.new/Kernel.#proc を使う方法などもあります。ブロックが与えられなかった場合のブロック引数の値はnilです。
def foo(cnt, &block_arg)
cnt.times { block_arg.call } # ブロックに収まったProcオブジェクトはcallで実行
end
foo(3) { print "Ruby! " } #=> Ruby! Ruby! Ruby!
メソッド定義において、仮引数はその種類毎に以下の順序でしか指定することはできません。いずれも省略することは可能です。
- デフォルト式のない引数(複数指定可)
- デフォルト式のある引数(複数指定可)
- * を伴う引数(1つだけ指定可)
- デフォルト式のない引数(複数指定可)
- キーワード引数(複数指定可)
- ** を伴う引数(1つだけ指定可)
- & を伴う引数(1つだけ指定可)
# すべて持つ(極端な例なのでおすすめしない)
def f(a, b, c, m = 1, n = 1, *rest, x, y, z, k: 1, **kwrest, &blk)
puts "a: %p" % a
puts "b: %p" % b
puts "c: %p" % c
puts "m: %p" % m
puts "n: %p" % n
puts "rest: %p" % [rest]
puts "x: %p" % x
puts "y: %p" % y
puts "z: %p" % z
puts "k: %p" % k
puts "kwrest: %p" % kwrest
puts "blk: %p" % blk
end
f("a", "b", "c", 2, 3, "foo", "bar", "baz", "x", "y", "z", k: 42, u: "unknown") { }
#=> a: "a"
# b: "b"
# c: "c"
# m: 2
# n: 3
# rest: ["foo", "bar", "baz"]
# x: "x"
# y: "y"
# z: "z"
# k: 42
# kwrest: {:u=>"unknown"}
# blk: #<Proc:0x007f7e7d8dd6c0@-:16>
# yield を使う
def foo
# block_given? は、メソッドがブロックを渡されて
# 呼ばれたかどうかを判定する組み込み関数
if block_given?
yield(1,2)
end
end
# Proc.new を使う
def bar
if block_given?
Proc.new.call(1,2) # proc.call(1,2) でも同じ(proc は組み込み関数)
end
end
# 応用: 引数として Proc オブジェクトとブロックの
# 両方を受け付けるイテレータを定義する例
def foo(pr = nil, &block)
pr = pr || block
pr.call(1,2)
end
foo(proc {|a,b| p [a,b]})
foo {|a,b| p [a,b]}
# ブロック引数を使う
def baz(&block)
if block
block.call(1,2)
end
end
またメソッド実行時の例外を捕捉するために begin 式と同様のrescue, else, ensure 節を指定できます。例外処理については制御構造/begin参照。
メソッド定義式は、メソッド名を Symbol にしたオブジェクトを返します。
@see https://magazine.rubyist.net/articles/0041/0041-200Special-kwarg.html
演算子式の定義
演算子式において、「再定義可能な演算子」に分類された演算子の実装はメソッドなので、定義することが可能です。
これらの演算子式を定義する例を以下に挙げます。
# 二項演算子
def +(other); end # obj + other
def -(other); end # obj - other
# 単項プラス/マイナス
def +@; end # +obj
def -@; end # -obj
# 要素代入
def foo=(value); end # obj.foo = value
# [] と []=
def [](key); end # obj[key]
def []=(key, value); end # obj[key] = value
def []=(key, key2, value); end # obj[key, key2] = value
# バッククォート記法
def `(arg); end # `arg` または %x(arg)
バッククォート記法の実装はメソッドなのでこのように再定義が可能です。普通はこのメソッドを再定義するべきではありませんが、まれにOS(シェル)のコマンド実行の挙動に不具合がある場合などに利用できます。
メソッド定義のネスト
ネスト可能です。ネストされた定義式は、それを定義したメソッドが実行された時に定義されます。このことを除けば、普通のメソッド定義式と同じです。以下の例を参照してください。
class Foo
def foo
def bar
p :bar
end
end
def Foo.method_added(name)
puts "method \"#{name}\" was added"
end
end
obj = Foo.new
obj.bar rescue nil # => undefined method `bar' for #<Foo:0x4019eda4>
obj.foo # => method "bar" was added
obj.foo # => warning: method redefined; discarding old bar
Foo.new.bar # => :bar (他のインスタンスでも定義済み)
メソッドの評価
メソッドが呼び出されると、以下の順で式が評価されます。
- 指定されていれば引数のデフォルト式
- メソッドの本体 body
- 指定されていれば例外の発生の有無によりメソッド定義式の rescue 節または else 節
- 指定されていれば ensure 節
引数のデフォルト式も含め、すべてそのメソッドのコンテキストで評価されます。
メソッドの戻り値は return に渡した値です。return が呼び出されなかった場合は、 body の最後の式の値を返します。 body の最後の式が値を返さない式の場合は nil を返します。
またメソッドは定義する前に呼び出すことはできません。例えば
foo # <- foo は未定義
def foo
print "foo\n"
end
は未定義メソッドの呼び出しで例外 NameError を発生させます。
特異メソッド定義
def foo.test
print "this is foo\n"
end
文法:
def 式 `.' 識別子 [`(' [引数 [`=' default]] ... [`,' `*' 引数 ]`)'] 式.. [rescue [error_type,..] [=> evar] [then] 式..].. [else 式..] [ensure 式..] end
特異メソッドとはクラスではなくある特定のオブジェクトに固有のメソッドです。特異メソッドの定義はネストできます。
クラスの特異メソッドはそのサブクラスにも継承されます。言い替えればクラスの特異メソッドは他のオブジェクト指向システムにおけるクラスメソッドの働きをすることになります。
特異メソッド定義式は、メソッド名を Symbol にしたオブジェクトを返します。
クラスメソッドの定義
Ruby におけるクラスメソッドとはクラスの特異メソッドのことです。Ruby では、クラスもオブジェクトなので、普通のオブジェクトと同様に特異メソッドを定義できます。
したがって、何らかの方法でクラスオブジェクトにメソッドを定義すれば、それがクラスメソッドとなります。具体的には以下のようにして定義することが出来ます(モジュールも同様です)。
# 特異メソッド方式。
class Hoge
def Hoge.foo
end
end
# クラス定義の外でも良い
def Hoge.bar
end
# 以下のようにすればクラス名が変わってもメソッド部の変更が不要
class Hoge
def self.baz
'To infinity and beyond!'
end
end
# 特異クラス方式。複数のメソッドを一度に定義するとき向き
class << Hoge
def bar
'bar'
end
end
# モジュールをクラスに extend すれば、モジュールのインスタンス
# メソッドがクラスメソッドになる
module Foo
def foo
end
end
class Hoge
extend Foo
end
extend については、Object#extend を参照してください。
呼び出し制限
メソッドは public、private、protected の三通りの呼び出し制限を持ちます。
- public に設定されたメソッドは制限なしに呼び出せます。
- private に設定されたメソッドは関数形式でしか呼び出せません。 ただし self.foo のように self. と書かれている場合は呼び出すことができます。
- protected に設定されたメソッドは、そのメソッドを持つオブジェクトが selfであるコンテキスト(メソッド定義式やinstance_eval)でのみ呼び出せ ます。
class Foo
def foo
p caller.last
end
protected :foo
end
obj = Foo.new
# そのままでは呼べない
obj.foo rescue nil # => -:11 - protected method `foo' called for #<Foo:0x401a1860> (NameError)
# クラス定義内でも呼べない
class Foo
Foo.new.foo rescue nil # => -:15 - protected method `foo' called for #<Foo:0x4019eea8>
# メソッド定義式内で呼べる
def bar
self.foo
end
end
Foo.new.bar # => ["-:21"]
# 特異メソッド定義式内でも呼べる
def obj.bar
self.foo rescue nil
end
obj.bar # => ["-:27"]
デフォルトでは def 式がクラス定義の外(トップレベル)にあれば private、クラス定義の中にあれば public に定義します。これは Module#public、Module#private、 Module#protected を用いて変更できます。ただし Object#initialize という名前のメソッドと Object#initialize_copy という名前のメソッドは定義する場所に関係なく常に private になります。
def foo # デフォルトは private
end
class C
def bar # デフォルトは public
end
def ok # デフォルトは public
end
private :ok # …だが、ここで private に変わる
def initialize # initialize は private
end
end
private と protected は同じ目的(そのメソッドを隠し外から呼べないようにする)で使用されますが、以下のような例では、private は使えず、protected を利用する必要があります。正確には、private には関数を定義する目的があるが、呼び出し制限の目的でも(ここに挙げた制限があるにもかかわらず) protected よりは private が使われることの方が多いようです。
class Foo
def _val
'val'
end
protected :_val
def op(other)
# other も Foo のインスタンスを想定
# _val が private だと関数形式でしか呼べないため
# このように利用できない
self._val + other._val
end
end
定義に関する操作
alias
alias foo bar
alias :foo :bar
alias $MATCH $&
文法:
alias 新メソッド名 旧メソッド名 alias 新グローバル変数名 旧グローバル変数名
メソッドあるいはグローバル変数に別名をつけます。メソッド名には識別子そのものか リテラル/シンボル を指定します(obj.method のような式を書くことはできません)。alias の引数はメソッド呼び出し等の一切の評価は行われません。
メソッドの定義内で別名を付けるにはModuleクラスのメソッド Module#alias_method を利用して下さい。
別名を付けられたメソッドは、その時点でのメソッド定義を引き継ぎ、元のメソッドが再定義されても、再定義前の古いメソッドと同じ働きをします。あるメソッドの動作を変え、再定義するメソッドで元のメソッドの結果を利用したいときなどに利用されます。
# メソッド foo を定義
def foo
"foo"
end
# 別名を設定(メソッド定義の待避)
alias :_orig_foo :foo
# foo を再定義(元の定義を利用)
def foo
_orig_foo * 2
end
p foo # => "foofoo"
グローバル変数の alias を設定するとまったく同じ変数が定義されます。このことは一方の変数への代入は他方の変数にも反映されるようになることを意味します。
# 特殊な変数のエイリアスは一方の変更が他方に反映される
$_ = 1
alias $foo $_
$_ = 2
p [$foo, $_] # => [2, 2]
$bar = 3
alias $foo $bar
$bar = 4
p [$foo, $bar] # => [4, 4]
ただし、正規表現の部分文字列に対応する変数 $1,$2, ... には別名を付けることができません。また、インタプリタに対して重要な意味のあるグローバル変数 (変数と定数 を参照)を再定義すると動作に支障を来す場合があります。
alias 式は nil を返します。
undef
undef bar
文法:
undef メソッド名[, メソッド名[, ...]]
メソッドの定義を取り消します。メソッド名には識別子そのものか リテラル/シンボル を指定します(obj.method のような式を書くことはできません)。 undef の引数はメソッド呼び出し等の一切の評価は行われません。
メソッドの定義内で定義を取り消すにはModuleクラスのメソッド Module#undef_method を利用して下さい。
undef のより正確な動作は、メソッド名とメソッド定義との関係を取り除き、そのメソッド名を特殊な定義と関連づけます。この状態のメソッドの呼び出しはたとえスーパークラスに同名のメソッドがあっても例外 NameError を発生させます。 (一方、メソッド Module#remove_method は、関係を取り除くだけです。この違いは重要です)。
alias による別名定義と undef による定義取り消しによってクラスのインタフェースをスーパークラスと独立に変更することができます。ただし、メソッドが self にメッセージを送っている場合もあるので、よく注意しないと既存のメソッドが動作しなくなる可能性があります。
undef 式は nil を返します。
defined?
defined? print
defined? File.print
defined?(foobar)
defined?($foobar)
defined?(@foobar)
defined?(Foobar)
文法:
defined? 式
式が定義されていなければ、nil を返します。定義されていれば式の種別を表す文字列を返します。
定義されていないメソッド、undef されたメソッド、Module#remove_method により削除されたメソッドのいずれに対しても defined? は nil を返します。
特別な用法として以下があります。
defined? yield
yield の呼び出しが可能なら真(文字列 "yield")を返します。 Kernel.#block_given? と同様にメソッドがブロック付きで呼ばれたかを判断する方法になります。
defined? super
super の実行が可能なら真(文字列 "super")を返します。
defined? a = 1
p a # => nil
"assignment" を返します。実際に代入は行いませんがローカル変数は定義されます。
/(.)/ =~ "foo"
defined? $& # => "global-variable"
defined? $1 # => "global-variable"
defined? $2 # => nil
$&, $1, $2, などは直前のマッチの結果値が設定された場合だけ真を返します。
def Foo(a,b)
end
p defined? Foo # => nil
p defined? Foo() # => "method"
Foo = 1
p defined? Foo # => "constant"
大文字で始まるメソッド名に対しては () を明示しなければ定数の判定を行ってしまいます。
以下は、defined? が返す値の一覧です。
- "super"
- "method"
- "yield"
- "self"
- "nil"
- "true"
- "false"
- "assignment"
- "local-variable"
- "local-variable(in-block)"
- "global-variable"
- "instance-variable"
- "constant"
- "class variable"
- "expression"