Ruby言語について、当面第一部を理解するのに必要な知識だけ簡単に解説して おく。プログラミング上のテクニックであるとか注意すべきところなんてもの は全く指摘しないので、この章を読んだからと言ってRubyプログラムが書ける ようになるわけではない。既にRuby言語の経験がある読者はこの章は飛ばし て問題ないだろう。
また文法については第二部で嫌というほど話すから、この章ではできるだけ文 法の詳細には関らないようにする。ハッシュリテラルなどバリエーションがあ る表記は最も多用されるもののみ示す。省略可能なものは原則的に省略せずに 示す。そのほうが構文規則がシンプルになるからだ。いちいち「省略できる」 とも言わない。
Rubyプログラムが操作できるものは全てオブジェクトである。Javaのint
や
long
のような「基本型(primitive)」はない。例えば以下のように書くと
それは「content
」
という内容を持った文字列オブジェクト(String
オブジェクト)である。
"content"
単に文字列オブジェクトと言ったが、正確に言えばこれは文字列オブジェクト を生成する「式」である。だから、何回も書けばそのたびに別の文字列オブジェ クトが生成される。
"content" "content" "content"
ここでは「content
」という内容を持った文字列オブジェクトが三つ生成される。
ところで、オブジェクトがそこにあるだけではプログラマには見え ない。オブジェクトを端末に表示する方法を教えておこう。
p("content") # "content"と表示される
「#
」以降はコメントだ。今後、結果はコメントとして付けておくことに
する。
「p(
……)
」は関数p
の呼び出しである。任意のオブジェクトを「そ
れっぽく」表示してくれる。基本的にデバッグ用の関数だ。
厳密に言うとRubyには関数はないのだが、いまは関数と考えておいてよい。関 数はどこにいても使うことができる。
さて、直接オブジェクトを生成する式(リテラル)をもう少し説明しておこう。 まずは一般的なところで、整数と小数だ。
# 整数 1 2 100 9999999999999999999999999 # どんなに大きな数でも使える # 小数 1.0 99.999 1.3e4 # 1.3×10^4
これも全てオブジェクトを生成する式であることを忘れないでほしい。 繰り返すが、Rubyには「基本型」はない。
以下の表現は配列オブジェクトを生成する。
[1, 2, 3]
このプログラムは整数1 2 3の三つをその順番で要素として持つ配列を生成す る。配列の要素には任意のオブジェクトが使えるので、こんなこともできる。
[1, "string", 2, ["nested", "array"]]
さらに、以下の表現はハッシュテーブルを生成する。
{"key"=>"value", "key2"=>"value2", "key3"=>"value3"}
ハッシュテーブルというのは、任意のオブジェクト同士の一対一対応を表す構 造だ。上記のように書くと次のような関係を記憶したテーブルができる。
"key" → "value" "key2" → "value2" "key3" → "value3"
こうやって作ったハッシュテーブルオブジェクトに「"key"
に対応付けられて
るのは何?」と聞けば、「"value"
だよ」と教えてもらえる。どうやって聞
くか。それにはメソッドを使う。
オブジェクトに対してはメソッドが呼べる。C++用語ではメンバ関数である。 メソッドとは何か、という説明は必要ないと思うので記法だけ簡単に解説す る。
"content".upcase()
ここでは文字列オブジェクト(内容は「content」)に対してupcase
メソッド
を呼び出している。upcase
はアルファベットの小文字を大文字にした新しい文
字列を返すメソッドなので、次のような結果になる。
p("content".upcase()) # "CONTENT"と表示される
メソッド呼び出しは連鎖してよい。
"content".upcase().downcase()
この場合、"content".upcase()
の返り値のオブジェクトに対してdowncase
メソッ
ドを呼ぶ、ということになる。
またJavaやC++のような公開フィールド(メンバ変数)はない。オブジェクト のインターフェイスはメソッドのみである。
Rubyでは式をいきなり書けばそれがプログラムになる。
C++やJavaのようにmain()
を定義する必要はない。
p("content")
これだけで完結したRubyプログラムである。このリストをfirst.rb
という
ファイルに入れたとすると、コマンドラインから次のように実行できる。
% ruby first.rb "content"
ruby
コマンドの-e
オプションを使えばいちいちファイルを作る必要すらない。
% ruby -e 'p("content")' "content"
ところで、p
が書いてある場所はプログラムのネストレベルが一番低い、
つまりプログラム的には一番「上」なので、「トップレベル」と呼ばれている。
トップレベルがあるのはRubyのスクリプト言語としての大きな特徴だ。
Rubyは基本的に一行が一文である。終端のセミコロンなどは必要ない。 だから以下のプログラムは三文と解釈される。
p("content") p("content".upcase()) p("CONTENT".downcase())
実行すればこうなる。
% ruby second.rb "content" "CONTENT" "content"
Rubyでは変数や定数は全てオブジェクトへの参照(reference
)を
保持している。だから別の変数に代入しただけでコピーが起こったりはしない。
Javaならオブジェクト型変数、C++ならオブジェクトへのポインタを考えれば
いいだろう。ただしそのポインタ自体の値は変更できない。
Rubyでは変数名の一文字目で変数の種別(スコープ)がわかるようになってい
る。アルファベット小文字かアンダーバーで始まるのがローカル変数だ。そし
て「=
」を使って代入を書ける。
str = "content" arr = [1,2,3]
最初の代入が変数宣言を兼ねるので宣言は必要ない。また変数には 型がないのでどんなオブジェクトだろうと無差別に代入できる。 以下のプログラムは全く合法である。
lvar = "content" lvar = [1,2,3] lvar = 1
もっとも、やれるからと言ってやる必要はない。いろいろな種類のオブジェク トを一つの変数にごっちゃに入れたりすれば普通は読みにくくなるものだ。現 実にあるRubyプログラムなら、こういうことは滅多にしない。これはあくまで 例のための例である。
変数参照もごく常識的な記法だ。
str = "content" p(str) # "content"が表示される
それから変数がリファレンスを保持するという点を例で見ておこう。
a = "content" b = a c = b
このプログラムを実行したあと、a b c
三つのローカル変数が指しているのは
同じオブジェクトで、一行目の"content"
で生成した文字列オブジェクトであ
る図1。
図1: Rubyの変数はオブジェクトへの参照を保持する
ところで、ローカルと言うからにはどこかに対してローカルでなければならな いはずだが、しばらく後にならないとこの範囲を言うことができない。とりあ えずトップレベルは一つの「ローカル」スコープだ、ということだけ言ってお こう。
変数名が大文字で始まるのが定数である。定数と言うからには(最初の)一回 しか代入できないものだ。
Const = "content" PI = 3.1415926535 p(Const) # "content"と表示される
二回代入するとエラーになる。と言いたいのだが、実は警告だけでエラーにな らない。これは、Rubyプログラム自体を操作するアプリケーション、例えば 開発環境などで、同じファイルを二回ロードしたときにエラーにならないよう にするためだ。つまり実用のためにやむを得ず認めているだけで、本当はエラー にしたいのである。実際にもバージョン1.1まではエラーになっていた。
C = 1 C = 2 # 現実には警告が出るだけだが、理念としてはエラー
さて、定数という言葉で騙される人が多いのだが、定数というのは「いったん
指すオブジェクトを記憶したら二度と変えない」という意味である。定数の指
すオブジェクトそれ自体が変わらないわけではない。英語で言うなら、
constantよりもread onlyのほうがよりよく意図を示しているだろう
(図2)。
ちなみにオブジェクト自体が変化しないよう指示するにはfreeze
という
別の方法を使う。
図2: 定数はread onlyの意味
そして実は定数のスコープについてもまだ話せない。次の節でクラスとからめて 話すことにする。
Rubyは制御構造が豊富で並べるだけでも大変なので、
とりあえずif
とwhile
はある、ということだけ言っておく。
if i < 10 then # 本体 end while i < 10 do # 本体 end
条件式ではfalse
とnil
という二つのオブジェクトだけが偽で、
残りのあらゆるオブジェクトは真になる。0や空文字列ももちろん真だ。
それとついでに、false
だけでは収まりが悪いからtrue
もある。
これも当然真だ。
本来オブジェクト指向システムにおいてメソッドとはオブジェクトに所属する ものだ。だがそれはあくまで理想の世界でのこと。普通のプログラムなら同じ メソッドの集合を持つオブジェクトがたくさんあるわけで、もしバカ正直にオブ ジェクト単位で呼び出せるメソッドを記憶していたりしたら大変なことになっ てしまう。そこで普通はクラスとかマルチメソッドのような仕組みを利用し て定義の重複をなくすわけだ。
Rubyでは、オブジェクトとメソッドを結びつける仕組みとして伝統的な「クラス」 の概念を採用している。即ち全てのオブジェクトはただ一つのクラスに所属 しており、呼び出せるメソッドをクラスで決める。このときオブジェクトのこ とを「○○クラスのインスタンス(instance)」と言うのだった。
例えば文字列"str"
はString
クラスのインスタンスである。そしてその
String
クラスにはupcase
、downcase
、strip
、その他いろいろなメソッド
が定義されているので、あたかも全ての文字列オブジェクトがこれらのメソッドに
反応できるかのように見えるわけだ。
# みんなStringクラスに属するので同じメソッドが定義されている "content".upcase() "This is a pen.".upcase() "chapter II".upcase() "content".length() "This is a pen.".length() "chapter II".length()
ところで、呼び出したメソッドが定義されていなかったらどうなるのだろう。
静的な言語ならコンパイルエラーになるところだが、Rubyでは実行時例外にな
る。実際に試してみよう。この程度の長さなら-e
でプログラムを渡してしま
うのが便利だ。
% ruby -e '"str".bad_method()' -e:1: undefined method `bad_method' for "str":String (NoMethodError)
メソッドが見付からないときはNoMethodError
というエラーになるようだ。
それと最後に、いちいち「String
のupcase
メソッド」などと言うのは煩わ
しいので専用の記法を用意しておこう。「String#upcase
」で「String
ク
ラスに定義されたupcase
メソッド」を示すことにする。
ちなみに「String.upcase
」と書くとRubyの世界では全く別の意味を持つ。
それはどんな意味か。それは次の項で説明する。
ここまでは既に定義されているクラスの話だった。
独自のクラスももちろん定義できる。
クラスを定義するにはclass
文を使う。
class C end
これは新しいクラスC
の定義だ。定義したら次のように使うことができる。
class C end c = C.new() # クラスCのインスタンスを作成し変数cに代入
インスタンスを作成する記法はnew C
ではないので注意。うーん、なんだか
C.new()
という記法はメソッド呼び出しみたいだな、と思った読者は鋭い。
Rubyではオブジェクトを生成する式は単なるメソッド呼び出しなのである。
まずRubyにおいてクラス名とは定数名と同義である。ではクラス名と同名の定数
には何が入っているのか。実は、クラスが入っている。Rubyではプログラムが
さわれるものは全てオブジェクトだったから、クラスも当然オブジェクトとし
て表現されている。これをクラスオブジェクトと呼んでおこう。全てのクラス
オブジェクトはClass
クラスのインスタンスである。
つまりclass
文とは、新しいクラスオブジェクトを作り、クラス名と同名の定
数にそれを代入する、という操作である。一方インスタンスの生成とは、定数
を参照し、そのオブジェクトに対してメソッド(普通はnew
)を呼ぶ、という
操作である。以下のような例を見ると、インスタンスの生成が普通のメソッド
呼び出しとなんら変わらないことがよくわかるだろう。
S = "content" class C end S.upcase() # 定数Sの指すオブジェクトを得てメソッドupcaseを呼び出す C.new() # 定数Cの指すオブジェクトを得てメソッドnewを呼び出す
そんなわけでRubyではnew
は予約語ではない。
それから、作りたてのクラスのインスタンスでもp
できるということを書いて
おこう。
class C end c = C.new() p(c) # #<C:0x2acbd7e4>
さすがに文字列や整数のようにきれいに表示することはできないが、所属クラ スと内部IDを表示してくれる。ちなみにこのIDはオブジェクトを指すポインタ の値だったりする。
そうそう、すっかり忘れていたがメソッド名の記法についてだ。「Object.new
」
はクラスオブジェクトObject
それ自体に対して呼ぶメソッドnew
、の意味にな
る。「Object#new
」と「Object.new
」は全く別物なので厳密に区別しなければ
いけない。
obj = Object.new() # Object.new obj.new() # Object#new
現実にはObject#new
なんてメソッドは定義されていないので
このプログラムは二行目でエラーになる。あくまで表記の例として
受け取ってほしい。
クラスが定義できてもメソッドが定義できなかったら意味がない。
我々のクラスC
にもメソッドを定義してみよう。
class C def myupcase( str ) return str.upcase() end end
メソッドを定義するにはdef
文を使う。この例ではmyupcase
という
メソッドを定義した。パラメータは一つで名前はstr
である。
変数のときと同じく、パラメータ変数や返り値の型を書く必要はない。
またパラメータの数はいくつでもよい。
定義したメソッドを使ってみる。メソッドはデフォルトで外から呼べる ようになっている。
c = C.new() result = c.myupcase("content") p(result) # "CONTENT"が表示される
もちろん慣れればいちいち代入する必要はない。以下のように書いても同じで ある。
p(C.new().myupcase("content")) # 同じく"CONTENT"が表示される
self
メソッド実行中は常に自分自身(メソッドが呼び出されたインスタンス)が
誰であるかという情報が保存されており、self
でその情報を取ることができる。
C++やJavaで言えばthis
だ。確かめてみよう。
class C def get_self() return self end end c = C.new() p(c) # #<C:0x40274e44> p(c.get_self()) # #<C:0x40274e44>
見てのとおり、二つの式は全く同一のオブジェクトを返している。
つまりc
に対するメソッド呼び出しの中にいるときにはself
はc
であることが
確認できた。
では自分自身に対してメソッドを呼ぶにはどうしたらいいだろうか。
まずself
経由で呼ぶ方法が考えられる。
class C def my_p( obj ) self.real_my_p(obj) # 自分自身に対してメソッドを呼ぶ end def real_my_p( obj ) p(obj) end end C.new().my_p(1) # 1を表示
しかし自分自身のメソッドを呼ぶのに「自分の」とわざわざ指定しなければな
らないのは面倒だ。それでself
に対する呼び出しのときはメソッドを呼び出す
対象のオブジェクト(レシーバreceiver)を省略できるようになっている。
class C def my_p( obj ) real_my_p(obj) # レシーバ指定なしで呼べる end def real_my_p( obj ) p(obj) end end C.new().my_p(1) # 1を表示
オブジェクトはデータ + コードだ、という表現もあるくらいで、メソッドが定 義できるだけではあまり役に立たない。オブジェクト単位でデータを記憶でき る必要がある。即ちインスタンス変数である。C++で言えばメンバ変数だ。
Rubyの変数名ルール通り、一文字目が種別を決める。インスタンス変数は「@
」
だ。
class C def set_i(value) @i = value end def get_i() return @i end end c = C.new() c.set_i("ok") p(c.get_i()) # "ok"を表示する
インスタンス変数はこれまでの変数とはちょっと変わっていて、代入しなくて も(定義しなくても)参照できる。その場合どうなるかというと……前のコー ドの続きとしてこんなことをやってみた。
c = C.new() p(c.get_i()) # nilと表示される
set
せずにget
してみたら、nil
と表示された。nil
というのは「ない」
を意味するオブジェクトである。そこにオブジェクトがあるのに
ないというのも不思議だが、そういうものなのだからしかたがない。
nil
はリテラルのように使うこともできる。
p(nil) # nilと表示される
initialize
これまで見てきたように、定義したばかりのクラスでもnew
を呼べばインスタ
ンスが作れる。それは確かにそうなのだが、クラス特有の初期化をしたいこと
もあるだろう。そういうときはnew
を変えるのではなくて、initialize
という
メソッドを定義しておく。そうするとnew
の中でそれを呼んでくれる。
class C def initialize() @i = "ok" end def get_i() return @i end end c = C.new() p(c.get_i()) # "ok"と表示される
ただ厳密に言うならこれはあくまでnew
というメソッドの仕様であって
言語の仕様ではない。
クラスは他のクラスを継承できる。例えばString
クラスはObject
クラスを
継承している。本書ではこの関係を図3のように縦の矢印で書く。
図3: 継承
この図の場合、継承されるほうのクラス(Object
)はスーパークラスとか
上位クラスと呼ぶ。継承したクラス(String
)はサブクラスまたは
下位クラスと呼ぶ。この点C++とは用語が違うので注意してほしい。
Javaとは同じだ。
とにかく試してみよう。我々の作ったクラスにも他のクラスを継承させてみる。 クラスを継承して作るには(スーパークラスを指定するには)次のように書く。
class C < SuperClassName end
これまでのようにスーパークラスを省略して書いたときは暗黙のうちに
Object
というクラスがスーパークラスになる。
さてなんのために継承するかと言えば、もちろんメソッドを引き継ぐためだ。 引き継ぐというのは、あたかもスーパークラスでのメソッド定義をサブクラス でもう一度繰り返したかのように働く、ということである。これも試してみよう。
class C def hello() return "hello" end end class Sub < C end sub = Sub.new() p(sub.hello()) # "hello"を表示する
hello
はクラスC
で定義されたメソッドだが、Sub
クラスのインスタンス
に対しても呼ぶことができた。もちろん今度も変数に代入する必要はない。以
下のように書いても同じだ。
p(Sub.new().hello())
同じ名前のメソッドを定義すればオーバーライドできる。C++や
Object Pascal(Delphi)では予約語virtual
などで明示的に指定したメソッド
しかオーバーライドできないが、Rubyでは全メソッドが無条件にオーバーライ
ド可能である。
class C def hello() return "Hello" end end class Sub < C def hello() return "Hello from Sub" end end p(Sub.new().hello()) # "Hello from Sub"を表示する p(C.new().hello()) # "Hello"を表示する
またクラスは何段継承してもよい。例えば図4のように。こ
の場合Fixnum
はObject
とNumeric
とInteger
のメソッド全て
を引き継ぐわけだ。同名のメソッドがあるときはより近いクラスのメソッドが
優先される。型によるオーバーロードなんてものは一切ないので条件は非常に
単純である。
図4: 多段の継承
それと、C++だと何も継承しないクラスというものを作れるが、Rubyでは必ず
Object
クラスを直接または間接に継承しなければならない。つまり継承関係の
図を書くとObject
を頂点とする一本のツリーになる。例えば基本ライブラリの
重要クラスの継承関係をツリーにすると図5のような感じだ。
図5: Rubyのクラスツリー
スーパークラスは一度(定義するときに)決めたらそれきりで、その後は 絶対に変えられない。つまりクラスツリーには新しいクラスが追加されること はあっても位置が移動したり削除されたりすることはない。
Rubyでは変数(インスタンス変数)は継承するものではない。継承しようにも、 そのクラスでどんな変数が使われるか、クラスには情報がないからだ。
だがメソッドさえ継承されればその継承したメソッドが呼ばれたときに(下位 クラスのインスタンスで)インスタンス変数への代入が起こる。つまり定義さ れる。そうすれば、インスタンス変数の名前空間はインスタンスごとに完全に フラットなので、どのクラスのメソッドからだろうとアクセスできるようにな る。
class A def initialize() # newの過程で呼ばれる @i = "ok" end end class B < A def print_i() p(@i) end end B.new().print_i() # "ok"と表示される
この挙動が納得できないならクラスと継承を潰して考えてしまうといい。クラ
スC
のインスタンスobj
があるとしたら、まずC
のスーパークラスのメソッ
ドが全部C
に定義されているとして考える。もちろんオーバーライドの規則
はちゃんと考慮しよう。そして今度はC
のメソッドをobj
にくっつけてしま
う(図6)。この強烈な「モノ感」がRubyのオブジェクト指向の
特徴である。
図6: Rubyのオブジェクトのイメージ
スーパークラスは一つしか指定できなかった。つまりRubyは見ためには 単一継承のように見える。しかし実際にはモジュールがあるために 多重継承と同等の能力を持つ。次にそのモジュールについて説明しよう。
モジュールを一言で言えば、スーパークラスを指定できず、インスタンスも 作れないクラスだ。定義は次のように書く。
module M end
これでモジュールM
が定義された。メソッドもクラスと全く同じに
定義できる。
module M def myupcase( str ) return str.upcase() end end
しかしインスタンスが作れないので直接は呼び出せない。そこでどうする かと言うと、他のクラスに「インクルード」して使う。すると あたかもそのモジュールをクラスが継承したかのように扱うことができる ようになる。
module M def myupcase( str ) return str.upcase() end end class C include M end p(C.new().myupcase("content")) # "CONTENT"と表示される
クラスC
では何のメソッドも定義していないのにmyupcase
を呼び出すことがで
きた。つまりこれはモジュールM
のメソッドを「継承」したということである。
インクルードは機能的には継承と全く同じものだ。メソッド定義もインスタン
ス変数のアクセスもなんら制限されない。
モジュールはスーパークラスを指定できない、と言ったが、 別のモジュールをインクルードすることならできる。
module M end module M2 include M end
つまりこれまた機能的にはスーパークラスを指定できるのに等しい。ただクラス が上位に来ることだけは決してない。あくまでモジュールの上にはモジュール だけが許される。
メソッドの継承も含めた例を以下に示す。
module OneMore def method_OneMore() p("OneMore") end end module M include OneMore def method_M() p("M") end end class C include M end C.new().method_M() # "M"が表示される C.new().method_OneMore() # "OneMore"が表示される
クラスと同じように継承を書けば図7のようになる。
図7: 多段のインクルード
ところでクラスC
にはスーパークラスもあるが、それとモジュールの関係はど
うなるのだろう。例えば次のような場合を考えよう。
# modcls.rb class Cls def test() return "class" end end module Mod def test() return "module" end end class C < Cls include Mod end p(B.new().test()) # "class"? "module"?
C
はCls
を継承し、Mod
をインクルードしている。この場合、表示されるのは
"class"
だろうか、"module"
だろうか。つまりモジュールとクラスのどちらが
より「近い」のだろうか。RubyのことはRubyに聞け、ということで実行してみる。
% ruby modcls.rb "module"
スーパークラスよりモジュールが優先されるようだ。
一般的に言うと、Rubyではモジュールをインクルードするとそのクラスと スーパークラスの「間」にはさまるような形で継承する。絵にするなら 図8のように書けるだろう。
図8: クラスとモジュールの相関関係
またモジュールにインクルードされているモジュールも考えれば 図9のようになる。
図9: クラスとモジュールの相関関係(2)
注意。この節は非常に重要で、しかも静的な言語だけを使ってきたプログラ マにはなじみにくい要素を説明している。他は斜め読みでもいいが、ここだけ は注意して読んでほしい。説明も比較的丁寧に行う。
まず定数について復習。大文字から始まるのが定数で、次のように して定義できた。
Const = 3
そしてこの定数を参照するときは次のようにするのだった。
p(Const) # 3を表示する
実はこれは次のようにも書ける。
p(::Const) # 同じく3を表示する
頭に::
が付くと「トップレベルで定義された定数である」ことを示す。ファイ
ルシステムのパスに例えてみるといい。ルートディレクトリにvmunix
というファ
イルがあったとしよう。/
にいるときは単にvmunix
と書けばアクセスできる。
またフルパスで/vmunix
と指定することもできる。Const
と::Const
もその関係
と同じである。トップレベルにいる間は単にConst
と書いてもいいし、フルパ
スで::Const
と書いてもいい。
ではファイルシステムのディレクトリにあたるものはRubyではなんだろうか。 それはクラス定義文とモジュール定義文である。しかし二つあると長くて面倒 なので以後はまとめてクラス定義文で代表することにしておこう。そのクラス 定義文中では定数レベルが上がる(ディレクトリの中に入る)。
class SomeClass Const = 3 end p(::SomeClass::Const) # 3と表示する p( SomeClass::Const) # 同じ。3と表示する
SomeClass
はトップレベルで定義されたクラス即ち定数なので単にSomeClass
と
書いても::SomeClass
と書いても参照できる。そしてそのクラス定義にネスト
している定数Const
は「SomeClass
の中にある」Const
ということで、
::SomeClass::Const
になる。
ディレクトリの中にまたディレクトリを作れるように、クラスの中でク ラスを作ることもできる。例えばこのように。
class C # ::C class C2 # ::C::C2 class C3 # ::C::C2::C3 end end end
ところで、クラス定義文の中で定義した定数は常にフルパスで書かなければいけな
いのだろうか。もちろんそんなことはない。ファイルシステムの例えと同じで、
同じレベルのクラス定義文の「なか」にいれば::
なしで参照できる。つま
りこういうことである。
class SomeClass Const = 3 p(Const) # 3と表示する end
あれっ、と思わなかっただろうか。なんと、クラス定義文中だろうと 実行されるプログラムが書けてしまうのである。静的な言語(だけ) に慣れている人だとこれは相当に意外だと思う。筆者も初めて見たときはギョッ とした。
いちおう付け加えておくと、もちろんメソッドの中からでも定数は見える。参 照規則はそのクラス定義文の中(メソッドの外)と全く同じである。
class C Const = "ok" def test() p(Const) end end C.new().test() # "ok"を表示
ここで全体を見据えて一つ書いておこう。 Rubyではプログラムのほとんどの部分が「実行される」。 定数定義・クラス定義文・メソッド定義文やその他 ほとんどのものは見た通りの順番で、実行される。
例えば次のコードを見てほしい。 今までに使った構造をいろいろ使ってみた。
1: p("first") 2: 3: class C < Object 4: Const = "in C" 5: 6: p(Const) 7: 8: def myupcase(str) 9: return str.upcase() 10: end 11: end 12: 13: p(C.new().myupcase("content"))
このプログラムは以下の順番で実行される。
1: p("first") | "first" と表示する。 | ||
3: < Object | 定数Object を参照しクラスオブジェクトObject を得る | ||
3: class C | Object をスーパークラスとする新しいクラスオブジェクトを生成、定数C に代入 | ||
4: Const = "in C" | ::C::Const を定義。値は"in C" | ||
6: p(Const) | ::C::Const を表示。"in C" を表示する。 | ||
8: def myupcase(...)...end | メソッドC#myupcase を定義。 | ||
13: C.new().myupcase(...) | 定数C を参照し、それに対しnew を呼び出し、さらにそれに対しmyupcase を呼び出す。 | ||
9: return str.upcase() | "CONTENT" を返す。 | ||
13: p(...) | "CONTENT" を表示。 |
これでようやくローカル変数のスコープについて話すことができる。
トップレベル、クラス定義文内、モジュール定義文内、メソッド本体、はそれぞれ
完全に独立したローカル変数スコープを持つ。つまり次のプログラムにあるlvar
は
全て別の変数であり、相互に行き来がない。
lvar = 'toplevel' class C lvar = 'in C' def method() lvar = 'in C#method' end end p(lvar) # "toplevel"と表示される module M lvar = 'in M' end p(lvar) # "toplevel"と表示される
self
以前、メソッドを実行中は自分自身(メソッドを呼び出したオブジェクト)が
self
になると言った。それは正しいのだが、半分でしかない。実はRubyプログ
ラム実行中はどこにいようとself
が設定されている。つまりトップレベルにも
クラス定義文にもself
があるのだ。
例えばトップレベルでもself
がある。トップレベルのself
は、main
という。
なんの変哲もない、Object
クラスのインスタンスだ。main
は
とりあえずself
を設定するために用意されているだけで、
それ自体にあまり深い意味があるわけではない。
そんなわけでトップレベルのself
即ちmain
はObject
のインスタンスなの
で、トップレベルでもObject
のメソッドが呼べるということになる。そして
Object
にはKernel
というモジュールがインクルードされており、そこで
p
やputs
などの「関数風メソッド」が定義されている
(図10)。だからこそトップレベルでもp
やputs
が呼べるのだ。
図10: main
とObject
、Kernel
だから本当はp
も関数ではなくメソッドなのである。ただ、Kernel
で定義され
ているため、どこにいようと、つまりself
のクラスがなんだろうと、「自分の」
メソッドとして関数のように呼べてしまうのだ。だからRubyには本当の意味で
の「関数」は存在しない。あるのはメソッドだけである。
ちなみにそういう関数風メソッドにはp
やputs
の他にも
print
puts
printf
sprintf
gets
fork
exec
などなど、
どこかで見たような
名前のものがたくさん存在する。このへんの名前の選びかたを見ると
Rubyの性格がなんとなく想像できるのではなかろうか。
さて、どこにでもself
が設定されているということはクラス定義文中にも
同じようにself
があるはずだ。クラス定義中のself
はそのクラス
(クラスオブジェクト)である。だからこんなふうになる。
class C p(self) # C end
これが一体何の役に立つのだろう。 実はもう非常に役立つ例を目にしている。これだ。
module M end class C include M end
実はこのinclude
はクラスオブジェクトC
に対するメソッド呼び出しである。ま
だ言っていないことだが、Rubyではメソッド呼び出しの括弧が省略できるのだ。
これまではクラス定義文の話が済んでいなかったので、メソッド呼び出しに見
えてしまわないようにあえて括弧を外しておいた。
Rubyではライブラリのロードも全て実行時に行われる。 通常はこう書く。
require("library_name")
見ためを裏切らずrequire
はメソッドである。予約語ですらない。こう書くと
こう書いた場所でロードが実行されて、そのライブラリ(のコード)に実行が
移る。Javaのパッケージのような概念はRubyにはないので、ライブラリ名の
名前空間を分離したいときはファイルをディレクトリに入れて分離する。
require("somelib/file1") require("somelib/file2")
そしてライブラリの中では普通class
文やmodule
文でもってクラスなどを定義
するわけだ。トップレベルの定数スコープはファイルの別に関係なくフラット
なので別のファイルで定義したクラスも最初から見える。クラス名の名前空間
を分離したいときは以下のように明示的にモジュールにネストさせる。
# net ライブラリの名前空間分離の例 module Net class SMTP # ... end class POP # ... end class HTTP # ... end end
これまで定数のスコープをファイルシステムに例え てきたが、ここからはその例えはすっかり忘れていただきたい。
定数にはまだまだ仕掛けがある。 まず、「外」のクラスにある定数も見ることができる。
Const = "ok" class C p(Const) # "ok"を表示 end
なぜこうなっているかと言うと、モジュールを名前空間として使うときに
便利だからだ。どういうことか、先程のnet
ライブラリの例に少し追加した
もので説明しよう。
module Net class SMTP # メソッドでNet::SMTPHelperを使う end class SMTPHelper # Net::SMTPを補助するクラス end end
こういう場合、SMTP
クラスの中からもSMTPHelper
と書いただけで
参照できるほうが便利ではないだろうか。
そこで「外のクラスが見えると便利」という結論になるわけだ。
「外」のクラスは何段ネストしていても参照できる。同じ名前の定数が複数のネ ストレベルで定義されているときは、内側から順番に見ていって最初に見付かっ たものが参照される。
Const = "far" class C Const = "near" # こちらのほうが上のConstより近い class C2 class C3 p(Const) # "near"を表示 end end end
また定数にはもう一つ検索パスがある。どんどん外のクラスを探していって トップレベルまで行っても見付からなかった場合は、さらに自分の スーパークラスの定数も見るのだ。
class A Const = "ok" end class B < A p(Const) # "ok"を表示 end
全く、ややこしいことこのうえない。
まとめよう。定数を探すときは、まず外のクラスを探し次にスーパークラスを 探す。例えば、かなり作為的だが、以下のようなクラス階層があったとする。
class A1 end class A2 < A1 end class A3 < A2 class B1 end class B2 < B1 end class B3 < B2 class C1 end class C2 < C1 end class C3 < C2 p(Const) end end end
C3
で定数Const
を参照すると図11の順番で検索される。
図11: 定数の検索順序
一点注意。外のクラスのスーパークラス、例えばA1
やB2
は全く検索されない。
検索するのは、外ならあくまで外方向に向かってのみ、スーパークラス
ならスーパークラス方向のみだ。でないととんでもない数のクラスを検索する
ことになりかねないし、そんなに複雑なものは挙動が予測できない。
オブジェクトならばメソッドを呼べる、と言った。呼べるメソッドはオブジェ クトのクラスで決まるとも言った。ならばクラスオブジェクトにも「そのクラス」 があるのだろうか(図12)。
図12: クラスのクラスは?
こういうときRubyでは実際に確かめてみることができる。
「自分の所属するクラス(クラスオブジェクト)を返す
メソッド」Object#class
があるからだ。
p("string".class()) # Stringと表示された p(String.class()) # Classと表示された p(Object.class()) # Classと表示された
String
はClass
というクラスに所属しているらしい。
ではさらにClass
のクラスはなんだろう。
p(Class.class()) # Classと表示された
またClass
らしい。つまりどんなオブジェクトだろうと
.class().class().class()
……と辿っていけばClass
に行き着き、
そこでループにはまってどんづまりになる(図13)。
図13: クラスのクラスのクラスの……
Class
はクラスのクラスだ。そして「○○の○○」という再帰構造を
持つもののことを「メタ○○」と言うから、Class
は「メタクラス」
である。
では今度は対象を変えて、モジュールについて考えてみる。モジュールもやは りオブジェクトなので、クラスと全く同じように「そのクラス」があるはずだ。 見てみよう。
module M end p(M.class()) # Moduleと表示される
モジュールオブジェクトのクラスはModule
と言うらしい。
ではModule
クラスのクラスは何だろう。
p(Module.class()) # Class
これまたClass
だ。
それなら今度は方向を変えて継承関係を調べてみる。
Class
とModule
のスーパークラスはなんだろうか。
RubyではClass#superclass
でそれを調べられる。
p(Class.superclass()) # Module p(Module.superclass()) # Object p(Object.superclass()) # nil
なんと、Class
はModule
の下位クラスであった。
これらの事実からRubyの重要クラスの関係を図示すると
図14のようになる。
図14: Rubyの重要クラスの関係
今まで何の説明もなくnew
やinclude
を使ってきたが、これでようやく正体を
説明できる。new
は実はClass
クラスで定義されたメソッドなのである。だから
どんなクラスでも(Class
のインスタンスだから)いきなりnew
が使えるわけだ。
しかしModule
にはnew
が定義されていないのでインスタンスが作れない。また
include
はModule
クラスで定義されているから、モジュールに対してもクラスに
対してもinclude
が呼べる。
この三つのクラス、Object
、Module
、Class
はRubyの根幹を支えるオブ
ジェクトたちだ。言ってみれば、この三つのオブジェクトがRubyのオブジェク
ト世界自体を記述しているわけだ。つまりオブジェクトを記述するオブジェク
トである。ならばObject Module Class
はRubyの「メタオブジェクト」だ。
オブジェクトならばメソッドを呼べる、と言った。呼べるメソッドはオブジェ クトのクラスで決まるとも言った。だが理念としてはメソッドはオブジェクトに 所属するのだということも言ったと思う。あくまでクラスは同じメソッドを 何度も定義してやる手間を省くための仕掛けだと。
そこでだ。実はRubyにはクラスとは関係なく個々のオブジェクト(インスタン ス)に対してメソッドを定義する仕組みもあるのだ。それにはこう書く。
obj = Object.new() def obj.my_first() puts("My first singleton method") end obj.my_first() # My first singleton methodと表示
知ってのとおりObject
は全クラスのルートである。そんな重要クラスに
my_first
などという変な名前のメソッドが定義されているはずがない。そし
てobj
はObject
のインスタンスである。しかしobj
に対してmy_first
と
いうメソッドを呼ぶことができる。つまり間違いなく、所属するクラスとは全
く全然なにも関係ないメソッドが定義されている。このような、オブジェクト
ごとに定義されたメソッドのことを特異メソッド(singleton method)と
言う。
特異メソッドはどんなときに使うか。まずJavaやC++で言う スタティックメソッドの ようなものを定義するときだ。つまりインスタンスを生成しなくても使えるメ ソッドである。こういうメソッドはRubyではクラスオブジェクトの特異メソッ ドとして表現されるのだ。
例えばUNIXのunlink
というシステムコールがある。ファイルのエントリをファ
イルシステムから削除する命令だ。RubyではこれをダイレクトにFile
クラスの
特異メソッドunlink
として使えるようにしてある。それを使ってみよう。
File.unlink("core") # コアダンプを消す
いちいち「File
オブジェクトの特異メソッドunlink
」と言うのは面倒なので、
これからは単に「File.unlink
」と書くことにする。間違えてこれを
「File#unlink
」と書いたり、逆に「File
で定義されたメソッドwrite
」を
「File.write
」と書いたりしないでほしい。
記法 | 呼び出せる対象 | 呼び出し例 | |||
File.unlink | File クラス自身 | File.unlink("core") | |||
File#write | File のインスタンス | f.write("str") |
クラス変数はruby
1.6から加わった、比較的新しい機能だ。定数と同じくクラ
スに所属しており、クラスと、そのインスタンスの両方から代入・参照できる。
例を見てみよう。変数名の頭を@@
にしたものがクラス変数である。
class C @@cvar = "ok" p(@@cvar) # "ok"を表示 def print_cvar() p(@@cvar) end end C.new().print_cvar() # "ok"を表示
クラス変数も最初の代入が定義を兼ねているので、次のように代入前に
参照すると実行時エラーになる。@
こそ付くものの、インスタンス変数
とは振舞いがまるで違うのだ。
% ruby -e ' class C @@cvar end ' -e:3: uninitialized class variable @@cvar in C (NameError)
ここではちょっと横着して-e
オプションでプログラムを与えてみた。
'
と'
で囲まれている三行がプログラムだ。
またクラス変数は継承する。言いかたを変えると、下位クラスから上位 クラスのクラス変数を代入・参照できる。
class A @@cvar = "ok" end class B < A p(@@cvar) # "ok"を表示 def print_cvar() p(@@cvar) end end B.new().print_cvar() # "ok"を表示
最後に、いちおうグローバル変数もある。プログラムのどこからでも代入・参
照できるのがグローバル変数だ。変数名一文字目を$
にしたのがグローバル変数
である。
$gvar = "global variable" p($gvar) # "global variable"と表示
グローバル変数はインスタンス変数と同じく、あらゆる名前が代入前に
定義されていると見做せる。つまり代入前に参照しても
nil
を返すだけでエラーにはならない。
御意見・御感想・誤殖の指摘などは 青木峰郎 <aamine@loveruby.net> までお願いします。
『Rubyソースコード完全解説』 はインプレスダイレクトで御予約・御購入いただけます (書籍紹介ページへ飛びます)。
Copyright (c) 2002-2004 Minero Aoki, All rights reserved.