5. クラスとモジュール

Ruby はいわゆる純粋なオブジェクト指向言語である。 厳密には議論の余地があるとはいえ,通俗的には あらゆるものがクラス (class) のインスタンスである。 すべてのクラスはモジュール (module) でもあるが, クラスでないモジュールも存在する。 そういったモジュールは直接自分自身のインスタンスを作ることができない。 モジュールは名前空間として,またはクラスへの mix-in として使われる。

mix-in については第3章で触れました。

クラスそれ自身はメタクラス (metaclass) である Class のインスタンスである。 モジュールそれ自身はメタクラス Module のインスタンスである。 ClassModule の派生クラスであり, ModuleObject の派生クラスである。 そして Object それ自身は Class のインスタンスである。

この構造のことは第1章でも触れました。 図にすると 5.4 節 の挿し絵の右半分になります。

新しいモジュールを定義するには,

module Foo
end

新しいクラスを定義するには,

class Bar
end

モジュール定義やクラス定義は「式」である。 ただし,結果の値は重要ではない。 定義の本体が実行され, 副作用としてモジュールやクラスへの参照を値とする定数 FooBar が定義されることが重要である。 どこに属する定数として定義されるかについては,E = 2.718281828459 など他の一般の定数定義と同じである。 例えば,トップレベルで定義したときは,Object に属する定数として モジュール名やクラス名が定義される。

クラス定義も,一般の規則にしたがい,改行のかわりにセミコロンを使うことができる。

class Bar; end

基底クラスを指定してクラスを定義するには,

class MyString < String
end

モジュールの名前空間の内側でクラスを定義するには,

module Foo
  class MyString
  end
end

クラス名は定数にすぎないから, モジュール内部のクラスは二重コロン記法で参照できる。

Foo::MyString.new
クラスやモジュールが定数であることと, 二重コロン記法によるアクセスは第3章で学びました。

メソッド呼出しにとっては,ドットと二重コロンは交換可能である。 メソッド呼出し x.fx::f と書いてもよい。 上記の例は Foo::MyString::new と書くことができる。 一方,定数 K には二重コロンだけが使用できる (例: x::K )。 もしも x.K と書いた場合は,もっぱらメソッド呼出しと解釈される。

二重コロン記法が C++ 由来であり,クラス・メソッドが概ね C++ の静的メンバ関数に相当することから, 二重コロンを定数のほか,クラス・メソッドに使う流儀もある。

5.1 メソッドの定義

定数と同じく,メソッドはモジュールかクラスに属する。 ruby のトップレベルで定義されたメソッドは, Object の private インスタンス・メソッドとなる (したがって,トップレベル自身を含むあらゆる文脈の self に対して実行できる)

クラス定義やモジュール定義の本体で定義されたメソッドは,普通は, そのクラスやモジュールの public インスタンス・メソッド となる (したがって,そのクラスのインスタンス,またはそのモジュールを mix-in したクラスのインスタンス, さらにその派生クラスのインスタンスに対して実行できる)

新しいメソッドを定義するには,キーワード def を使う。

class Bar
  def baz
  end
end

引数をとるメソッドを定義するには,仮引数名を与える。

def baz(a, b)
end

省略可能な引数をとるメソッドを定義するには,仮引数にデフォルト値を与える。

def baz(a, b=nil)
end

デフォルト値は,妥当な Ruby 式ならば何でもよいが, たいていは単純な値にする。

デフォルト値を指定する構文は Python と同じです。 ただし,Python と異なり,Ruby のデフォルト値は呼び出しごとに 新しく評価 されます。 これには長所と短所がありますが,たいていは Ruby のほうが人々の素朴な直感にマッチします。

メソッドはいわゆる残余引数 (rest argument) をとることもできる。 残余引数は,任意の仮引数名の前に * を与えて指定する。 残余引数は,普通の仮引数との対応にあぶれた残りの実引数をすべて集めて1個の配列にする。 残余引数は,末尾の引数として与える必要がある。ただし,後述のブロック引数を除く。

def baz(a, *rest)
end

のとき,メソッド呼出し baz(1, 2, 3) に対し, a1 になり, rest[2, 3] になる。

メソッドを呼び出すとき,実引数の配列の前に * を与えて, 1個の配列を別々の仮引数に分けて渡すことができる。

def baz(a, b, c, *rest)
  p a
  p b
  p c
  p rest
end

x = %w(Hot Tot Jin Jod Fie Fly Zan Zod Pik Snik Lun Lod Ichabod)
baz(*x)

を実行すると,このようになる。

$ jruby star.rb
"Hot"
"Tot"
"Jin"
["Jod", "Fie", "Fly", "Zan", "Zod", "Pik", "Snik", "Lun", "Lod", "Ichabod"]
$ 
baz(*x) のかわりに baz(13, :Thaegan, :children, *x) を呼び出したらどうなるでしょうか。 それはメソッド定義とメソッド呼出しの両方から * を消した場合と比べ,どのように同じで, どのように違うでしょうか。 (ヒント: object_id)

実引数としてハッシュを渡すとき,あいまいさがなければ波括弧を省略してよい。 例えば,

def baz(a, b)
end

に対して,こう呼び出してよい。 b{:a=>10, :b=>20} というハッシュになる。

baz "test", :a=>10, :b=>20

5.1.1 多重代入

メソッド呼出しで実引数から仮引数へ値が渡されるとき, 変数への代入と同じく,オブジェクトへの参照 (つまりポインタ) が渡される (どちらの場合も,内部実装としては,nil など特定のオブジェクトは 参照ではなくオブジェクトそのものが渡されるかもしれない。 しかし,そういった事情は Ruby プログラムからは安全に無視できる)。 引数渡しと代入演算は実質同じである。

この類似性から類推できるとおり, メソッド呼出しで複数の実引数値を複数の仮引数に渡せるように, 代入演算で複数の値を複数の変数に渡すことができる。

a, b = 1, 2            # a = 1; b = 2

右辺の式の並びは配列オブジェクトで代用できる。

a, b = [1, 2]          # a = 1; b = 2

メソッド呼出しの仮引数と実引数で * を指定できるのと同じく, 代入の左辺と右辺でも * を指定できる。

a, *b = [1, 2, 3, 4]   # a = 1; b = [2, 3, 4]
a, b, c = 1, *[2, 3]   # a = 1; b = 2; c = 3

ただし,メソッド呼出しでは引数の個数 (いわゆる arity) がマッチしないと引数エラー ArgumentError が発生するが,代入演算はそのように厳しくない。 左辺が多すぎるとき,あぶれた変数には nil が代入される。 右辺が多すぎるとき,あぶれた値は単に捨てられる。

このような多重代入の典型的な用法の一つは, メソッドからの複数の戻り値の受け取りである。

def f
  return 1, 2          # 配列 [1, 2] を構築して返す
end

x, y = f()             # x = 1; y = 2

5.2 クラスのためのメソッド

これまでの例はどれも,クラスのインスタンスから呼び出せるインスタンス・メソッドを定義してきた。 では,(new メソッドのように) クラス自身から呼び出せるメソッドはどうしたら定義できるだろうか?

この問題に対し,クラスに基づくオブジェクト指向言語としての正攻法は,メタクラスに対するメソッドとして定義する方法である。 メタクラスのインスタンス・メソッドは,クラスにとってのクラス・メソッドである。 Ruby のメタクラスは開いているから,いつでもメソッドを追加定義できる。

class Class
  def baz
    puts "this is baz"
  end
end

Array.baz
String.baz
Object.baz

すぐに分かるように,これは Ruby の すべてのクラス に対して等しく baz というクラス・メソッドを定義する。 いわば new メソッドと同格のメソッドを追加しているわけである。 これは Ruby という言語そのものを別の姿にしかねない強力な方法だが, おそらく君が日常的に望むものとは違っているだろう。

全クラスに対してではなく, C++ の静的メンバ関数や Java の静的メソッドのように あるクラスに対してだけ,クラス自身から呼び出せるメソッドを定義するには, どうすればよいだろうか?

Ruby には 特異メソッド (singleton method) という, ある特定の1個のインスタンスに専属するメソッド を定義する機構が用意されている。 これをメタクラスのインスタンスであるクラスに応用すればよい。 一般のインスタンスに対する特異メソッドについては次々節で述べる。

クラス Bar に対し,

def Bar.baz
end
class Bar
  def self.baz
  end
end
class <<Bar
  def baz
  end
end

この三つはどれも Bar に専属する特異メソッド baz を定義する。 たとえトップレベルにあっても def Bar.baz は グローバル関数ではなく Bar に専属する特異メソッドを定義する。 特異メソッドは

Bar.baz 

または

Bar::baz 

という構文で呼び出すことができる。 この呼出しで Bar はインスタンス扱いであり,レシーバの値は Bar である。

class Barend 内では self の値は Bar ですから, 上記の三つの書き方のうち,最初の二つの書き方は実は同じ方法です。 三つ目の書き方は,特異クラス (後述) を定義する専用の構文です。

5.3 mix-in の方法: include と extend

下記はモジュール Foo にインスタンス・メソッド baz を定義する。

module Foo
  def baz
  end
end

Foo はモジュールだから,自分ではインスタンスを作ることができず,そのメソッドを呼び出せない。 しかし,他の任意のクラスは,モジュール Fooinclude することにより, そのメソッド baz を呼び出すことができる。 include はメタクラス Module の private インスタンス・メソッドである (つまり,事実上,モジュールやクラスを定義する時,その定義本体内だけで呼び出せる関数である)。 慣例として include の呼出しでは丸括弧は省く。

class ARealClass
  include Foo
end

ARealClass.new.baz

このとき,モジュール Foo のすべてのインスタンス・メソッドが, ARealClass の任意のインスタンスをレシーバとして呼び出せる。 Foo にメソッドを追加したときは,そのメソッドも ARealClass の任意のインスタンスをレシーバとして呼び出せる。 メソッドだけでなく,定数も取り込まれる。 もしも FooK という定数が定義されていたら, 同じ定数に ARealClass::K でアクセスできる。 ARealClass でメソッドを定義したとき, そのメソッドの中からは単に K でアクセスできる。

このようなモジュールの取込みは mix-in と呼ばれ,Ruby ライブラリの多くの機能がこの方法で作成されている。 例えば,Array の興味深いメソッドの多くがモジュール Enumerable に由来している (第6章 参照)。

1個のインスタンスに対してだけモジュールを mix-in したいときは, Object のインスタンス・メソッド extend を使えばよい。

a = Object.new
a.extend Foo
a.baz

これはインスタンスに対して,モジュールのインスタンス・メソッドを特異メソッドとして追加する。

5.4 特異メソッドと特異クラス

5.2 節で述べたように,特異メソッドとは,ある特定の1個のインスタンスに専属するメソッドである。 5.2 節ではメタクラスのインスタンスであるクラスに対する特異メソッドを論じた。 5.3 節の extend は一般のインスタンスに特異メソッドをまとめて定義する高水準の方法である。

任意のインスタンス x に対し,特異メソッド f を定義する基礎的な方法は,

def x.f
end

または

class <<x
  def f
  end
end

である。 5.2 節で述べた方法は,x がたまたまクラス (=メタクラスのインスタンス) だった場合に該当する。

しかし,ここで君は矛盾を感じているはずだ。 5.1 節によれば Ruby では「定数と同じく,メソッドはモジュールかクラスに属する」はずだ。 そうだとすれば,メソッドが「ある特定の1個のインスタンスに専属する」ことは出来ないはずだ。

実際には,Ruby は特異メソッドを定義するとき,その裏側でひそかに 特異クラス (singleton class) と呼ばれるクラスを自動生成することによって, メソッドがモジュールかクラスに属するという一貫性を維持している。 特異クラスとは,ある特定の1個のインスタンスに固有の無名クラスである。 究極的には,Ruby におけるあらゆるインスタンスがそれ自身の特異クラスを持ち得る。

次の特異メソッド定義を例にして考えよう。

a = Object.new
def a.foo1
  puts "Hi"
end

a.foo1

このとき,特異メソッド foo1 は,実際にはインスタンス a に直接,専属するのではなく, インスタンス a のためだけに自動生成される特異クラス上に定義される。 つまり,特異メソッドとは,実は,特異クラスに属するメソッド である。

Ruby は特異クラスを,影の存在として,できるかぎり表に出さないように努めている。 メソッド呼出し a.class はあくまで表向きのクラスを返す。 影の存在である特異クラスへは普通はアクセスできない。 特異クラスに属するメソッドは,特異クラスが表に出ない限りは, 特異クラスと1対1に対応する特定の1個のインスタンスに専属すると見なせる。 これが特異メソッドの正体である。

次のコードは,オブジェクトの特異クラスへのアクセスを可能にする。 これは,特異メソッドを定義するための classend 構文の中で self が特異クラスであることを利用し,その値を定義本体の最後の値として取得する。

class Object
  def singleton_class
    class <<self
      self
    end
  end
end

メソッド singleton_class を Ruby の任意のオブジェクトに対して呼び出せば, その特異クラスを得ることができる。 ただし,欠点として,もともと特異クラスをもたないオブジェクトでも, このメソッドにかければ,特異クラスが自動生成される。

一般に任意の x について x.singleton_class の基底クラスは x.class です。 つまり, 特異クラスは,表向き属しているクラスの派生クラスとして自動生成されます。 確かめてみましょう。

5.5 定数と関数のための名前空間としてのモジュール

モジュールは名前空間として利用できる。 クラス名やモジュール名その他一般の定数について, モジュール内に定義を入れ子にし,2重コロン記法でアクセスできること, 繁雑さを避けるため,他の定数や変数で自由にショートカットを設けられることを, 君は第3章で学んだ。

しかし,まだ一つ大きな欠落がある。 それは関数定義だ。 もしも君がトップレベルのグローバルな定数定義と関数定義

K = 1
def baz
end

を,名前空間の分割のため,モジュールの中に入れ子にしたいと思ったとき,

module Foo
  K = 1
  def baz
  end
end

と,単純に module でくくっただけではうまく行かない。 インスタンス・メソッドを定義することになるからである。 モジュールは自分ではインスタンスを作れないから,どこかのクラスに mix-in してもらって, そのインスタンスで baz メソッドを呼び出すしかない。

しかし,特異メソッドを使えば,この問題は解決される。

module Foo
  K = 1
  def self.baz
  end
end

定数 KFoo::K としてアクセスできるのと同じく, baz メソッドを Foo::baz または Foo.baz として呼び出すことができる。

だが,これで十分だろうか?

モジュールをクラスに mix-in したときは,最初の例のようにインスタンス・メソッドとして単に baz と呼び出せるようにできないだろうか。 話を蒸し返しているようだが,これは決して大それた望みではない。 定数ならば,mix-in したとき,単に K と参照できるのだから,対称性を考えれば当然の要望である。

まさにこのことを行うメソッド module_function がメタクラス Module の private インスタンス・メソッドとして用意されている。

module Foo
  K = 1
  def baz
  end
  module_function :baz  
end

module_function は,実引数としてシンボルが与えられたとき, モジュールに対するその名前のインスタンス・メソッドをコピーして, 同じ名前でモジュールの特異メソッドを新しく定義するとともに, もとのインスタンス・メソッドを private にする (つまり,mix-in したクラスのインスタンスからレシーバを省略してのみ呼び出せるようにする)。

この場合, baz のメソッド本体における self は, それがモジュールの特異メソッドとして呼び出されたときはモジュール Foo になり, インスタンス・メソッドとして呼び出されたときはそのインスタンスになります。 この差が問題にならないように,module_function は, 対象が self に依存しない「関数」であることを前提とします。 module_method ではなく module_function という名前であることに注意しましょう。 もとのインスタンス・メソッドを private にすることは, ある意味では大きなお世話ですが,典型的な誤用を防ぐ効果があります。

module_function を無引数で呼び出すと, それ以降,そのモジュールで新しく定義されるメソッドすべてに,このような処理が行われる。

module Foo
  module_function
  K = 1
  def baz
  end
end

では,これで名前空間としてモジュールを使う方法は万全だろうか?

もしも君がここで何か実用的な規模のモジュールづくりに取り掛かったとしたら,おそらく困った事態になるだろう。

module APracticalModule
  module_function

  def baz
  end


  class Bar
    def initialize
      baz # ここで NameError: undefined local variable or method `baz' for …
    end
  end
end

bar = APracticalModule::Bar.new

入れ子のクラスのメソッドから,(典型的には共通ユーティリティである) 関数 baz を呼び出そうとしてもアクセスできない。 このことから分かるように, トップレベルでのグローバルな関数とクラスなどの定数の定義群を,単純に module でくくり, module_function を呼び出しただけでは,名前空間としてのモジュールへの閉じ込めはまだ不完全である。

一つの解決方法は,5.3 節で述べた include メソッドを使って, モジュールを入れ子のクラスに mix-in する方法である。

module APracticalModule
  module_function

  def baz
  end


  class Bar
    include APracticalModule  # 注目!
    def initialize
      baz
    end
  end
end

bar = APracticalModule::Bar.new

この例では入れ子のクラス定義が1個だけだから目立たないが,長いモジュール名が何度も繰り返されると, 君はうんざりするかもしれない。Emacs の動的略称展開などエディタによる入力補助があったとしても, あとでリストを読むときに不快感をおぼえるかもしれない。 だが,実用上,名前の衝突を避けるため,外部名となるモジュール名はあまり短くできない。

しかし,モジュール内に限れば,その中で矛盾しない限り,短い良い名前を自由に使える。 そもそも,そのための名前空間のはずだ。したがって,例えば,

module APracticalModule
  module_function
  AP = APracticalModule  # 注目!

  def baz
  end


  class Bar
    include AP  # 注目!
    def initialize
      baz
    end
  end
end

bar = APracticalModule::Bar.new

とできる。 もう一つの方法は,より素朴に AP::baz としてメソッドを参照する方法である。 モジュール名を短い名前にしているから,これも十分実用的である。

module APracticalModule
  module_function
  AP = APracticalModule  # 注目!

  def baz
  end


  class Bar
    def initialize
      AP::baz  # 注目!
    end
  end
end

bar = APracticalModule::Bar.new

この方法の実例は Ruby による Lisp インタープリタ L2Lisp.rb に見ることができる。

名前空間としてなるべく楽にモジュールを使う方法としては,現行の Ruby はここまでが精一杯です。 似たような性格の超高水準言語である Python の場合と異なり,書く人が少々意識的に手間をかける必要があります (Perl の場合 もそうですが, 人間ががんばれば弱点を克服できる,というのは, 高水準言語の設計としてはかなり失敗に近いことです) 派生クラスに対する情報隠蔽機能の欠如とあわせて, 大規模開発への適合性について Ruby は多少なりとも問題を抱えています。
しかし,Ruby はその簡潔さと記述力の高さから,しばしば開発の規模そのものを エレガントに小さくすることが可能です。 広範な問題に対し,これが Ruby の貧弱さを補います。 そして,その重要な要因となるのが次章で述べるイテレータです。 Ruby のイテレータは,その本来の目的に対しては Python や C# の同種の機構に比べて原始的で, いろいろと不自由ですが,それらと異なり,事実上,制御構文を拡張できるため, 日常的なプログラミングの快適さと,プログラムの簡潔さに大きく寄与します。


戻る


Copyright (c) 2007 OKI Software Co., Ltd.