4. 主なクラス

Ruby はさまざまな目的のために多くのクラスを用意している。 一般にクラスのインスタンスは,メタクラスのメソッド new によって構築されるが, 文字列など,特定のクラスのインスタンスは,特別な構文で構築することもできる。 ここでは主なクラスを紹介する。クラス名は定数だから大文字で始まる。

4.1 Object

Object はすべてのクラスの基底クラスである。

特に禁止しない限り Ruby のクラスは開いており,いつでも拡張可能だが, とりわけ Object クラスは,利用者による広域的な機能の追加場所として暗に陽に使われる。 君がトップレベルで定義したメソッドは,Object のインスタンス・メソッドとして事実上のグローバルな関数となる。 君がトップレベルで定義したクラスやモジュールその他の定数は,Object に属する定数としてグローバルに参照できる。

しかし,驚くべき非対称性として,トップレベルで定義した (小文字で始まる普通の) 変数は, トップレベルのフレームに属するローカル変数にすぎず,Object に追加されません。 もっとも,うかつに事実上のグローバル変数を定義できないようにするという意味では,これは好ましい仕様です。

Object の主要なインスタンス・メソッドを示す。

self == object
Java の Object#equals(Object) に相当する。同じ値かどうかを調べる一般的な述語関数。
self === object
case 文が比較に用いる述語関数。ここでの実装は == と同じ。 メタクラスはこれを右辺が左辺のインスタンスかどうかの述語に上書き定義している。 例: String === x は x が文字列ならば true。
equal?(object)
Java の == と同じ。 アイデンティティの一致を調べる。
object_id()
Java の System#identityHashCode(Object) に相当する。 オブジェクトごとに一意な整数値 (アイデンティティ) を返す。
class()
Java の Object#getClass() に相当する。 所属するクラスを返す。 ただし,特異メソッド実装のための内部的な特異クラスではなく, 表向きの公式な所属クラスを返す。
to_a()
オブジェクトを配列で表現した値。 これは廃止予定である。しかし,仮想的に配列とみなせるクラスは事実すべて Enumerable モジュールを mix-in しており,そこで to_a メソッドが定義されている。 したがって,多くのプログラムは廃止による影響を受けないと期待される。
to_s()
Java の Object#toString() に相当する。オプジェクトの一般的な文字列表現。
inspect()
オブジェクトの人間可読な,詳しい情報を含んだ文字列表現 (主にデバッグ用)。
freeze()
オブジェクトを凍結させ,これ以降の変更を禁止する。
send(method_name, arg...)
文字列やシンボルで与えられたメソッドを実行する。 例: [1, 2, 3].send("inspect")

トップレベルでこれらのメソッドと同じ名前の関数を適当に定義してみよう。 何が起こるだろうか。 メタクラスのインスタンスとしての Objectfreeze させてみよう (Object.freeze を実行すればよい)。 何が起こるだろうか。

クラスを定義するときは,たいていの場合,private インスタンス・メソッド initialize を適切に上書きする必要がある。

initialize()
C++/Java のコンストラクタに相当する。 メタクラスのメソッド new は,クラスの新しいインスタンスを構築するとき, 新インスタンスをレシーバとして initialize メソッドを呼び出す。
new メソッドは任意の引数をとる。new メソッドの引数がそのまま initialize メソッドに渡される。 Object での initialize の実装は,引数をとらず。何もしない。 派生クラスには,適切な仮引数宣言と適切なメソッド本体で initialize を上書き定義することが期待されている。

派生クラスの initialize メソッドの本体は,基底クラスとしての初期化を行うために super を呼び出すのが普通である。 特に C++/Java の経験者は下記の点に注意する必要がある。

もしも下記の例を C++/Java で書いたらどんな風になったか考えてみよう。

irb(main):001:0> class B; def initialize(x) puts "x = #{x}" end end
=> nil
irb(main):002:0> class D < B; end
=> nil
irb(main):003:0> D.new('Doom')
x = Doom
=> #<D:0x27e3f2>
irb(main):004:0> class E < D
irb(main):005:1>   def initialize(x, y)
irb(main):006:2>     super(x)
irb(main):007:2>     puts "y = #{y}"
irb(main):008:2>   end
irb(main):009:1> end
=> nil
irb(main):010:0> E.new('Doom', 'Dain')
x = Doom
y = Dain
=> #<E:0x720bea>
クラス定義で基底クラスを陽に指定しないとき (=暗に Object を指定したとき) は, initialize メソッドの中でわざわざ super を呼ばないのが普通です。 Objectinitialize は何もしませんから,問題はありません。

4.2 文字列 (String)

文字列のクラス名は String である。 文字列のインスタンスは,普通, ダブルクォートまたはシングルクォートで文字の列を囲むことで作られる。 どちらも文字の列の途中で自由に改行をしてよい。 両クォートの違いは利用できるエスケープ列の種類である。 ダブルクォートは,C/C++ の文字列で利用できるエスケープ列を利用でき, #{式} で任意の式を埋め込むこともできる。 シングルクォートは,\'\) 等のエスケープだけが利用可能である。

ダブルクォートは %Q(…) とも書ける。シングルクォートは %q(…) とも書ける。 ここで丸括弧のかわりに,波括弧やブラケットを使うこともできる。

"こんにちは, #{"world"}"

"1 足す 1 は
 … #{1+1} ですね"

'ここでは #{actor} は何もしない'

%[パーセントに直接,丸括弧,波括弧,またはブラケットを続けると %Q のように機能する]

%q(ここでは ' や "" を自由につかえる。

ただし,閉じ括弧は \) のようにする必要がある )

文字列は,配列と同じく,ブラケット [ ] による添え字付けで要素の参照または代入が可能である。

"Ruby"[1..-1]   # => "uby"

文字列は,配列と同じく,self   * integer で, 自分自身の内容を integer 回だけ繰り返した新しい文字列を作ることができる。

"Noradzeer, " * 3  # => "Noradzeer, Noradzeer, Noradzeer, "
この結果の末尾が気になる場合はこうします: (["Noradzeer"] * 3).join(", ")
["Noradzeer"] * 3 は長さ 1 の配列 ["Noradzeer"] の内容を3回繰り返して 長さ 3 の配列 ["Noradzeer", "Noradzeer", "Noradzeer"] を作ります。 .join(", ") は配列の各要素を ", " でつなげた文字列を作ります。 結果として "Noradzeer, Noradzeer, Noradzeer" になります。

文字列は,配列と同じく,self   + other で, other と連結した新しい文字列を作ることができる。

a = "Ru"        # => "Ru"
a + "by"        # => "Ruby"
a               # => "Ru"

文字列は,concat(other) や self   << other で 自分自身に other を連結させることができる。

a = "Ru"        # => "Ru"
a << "by"       # => "Ruby"
a               # => "Ruby"
配列にも concat(other) と self   << other がありますが,配列に他の配列を concat したときと << したときの結果は違います。 [1, 2, 3].concat([4, 5])[1, 2, 3, 4, 5] ですが, [1, 2, 3] << [4, 5][1, 2, 3, [4, 5]] です。

文字列での剰余演算 self   % [arg1, arg2, …] は sprintf(self, arg1, arg2, …) と同じである。 Ruby 独特の変換指定 %pinspect による文字列表現である。

qty = 100
qlty = "gold"
puts "%d %p coins to every finalist" % [qty, qlty] # 100 "gold" coins… と印字
この文字列に対する剰余演算は Python にちなんだ仕様です。 ただし,Python は,ANSI/ISO C 規格と矛盾する %p ではなく,%r によって詳しい文字列表現を得ます。

4.3 シンボル (Symbol)

シンボル (クラス名は Symbol) は文字列と似ているが,書換え不能であり, どのシンボルも同じ名前をもつ他のシンボルと equal? メソッドの判定で等しい。 つまり,シンボルは整数のように高速に同一性を判定できる。 アイデンティティというたかだか1ワード程度の整数を比較すればよいからである。

シンボルは Lisp にインスパイアされたものです。 シンボルはしばしば「記号」と呼ばれます。 人工知能の分野でしばしば君が耳にするはずの「記号計算」とは, (典型的には Lisp による) symbolic computation のことです。 実装上は,intern された Java の文字列と似ていますが, intern という概念も Lisp に由来します。 ちなみに Ruby で文字列を intern するとシンボルになります。

典型的には,C++/Java で enum による列挙定数を 順序付けなし に使うような用途に使う。 文字列よりも case 文などでの分岐が高速で, 整数よりも p などによる印字が分かりやすい。

シンボルはコロンを接頭して作る。

:reece

シンボルの名前に空白やハイフンその他の文字を含めるには,ダブルクォートで囲む。

:"First Ra-Kachar"

式を埋め込むこともできる。

:"Carn #{number}"

より一般には,文字列の intern メソッドでシンボルを作ることができる (同義語として to_sym も可)。シンボルから文字列を得るには to_s を使う。

irb(main):001:0> "ruby".intern
=> :ruby
irb(main):002:0> "ruby".intern.to_s
=> "ruby"

4.4 整数 (Integer) … Fixnum と Bignum

Ruby の整数は,固定ビット長で高速・省メモリな Fixnum と無限多倍長整数である Bignum で表現される。 両者の変換は透過的である。 Fixnum の演算結果がある値域を超えれば,結果は Bignum になり, Bignum の演算結果がある値域に収まれば,結果は Fixnum になる。 どちらも同じ演算をサポートする。 Integer を共通の基底クラスとする。

読みやすくするため,任意にアンダースコアを含めてよい。

1_000_000       # a million
100_0000        # 百万

16 進数,8 進数,2 進数でも表記できる。

0xD4            # 212
0324            # 212
0b1101_0100     # 212

4.5 浮動小数点数 (Float)

C++/Java の double に相当する倍精度浮動小数点数である。

0.01234
12_345.6        # 12345.6
-0.44e3         #  -440.0

4.6 配列 (Array)

配列 (クラス名は Array) は,Java の java.util.ArrayList, C++ の vector に相当する可変長の列であり, 各要素への定数時間でのランダムアクセスが可能である。

任意の型の値を要素にできる。必ずしも要素の型が斉一である必要はない。 添え字により要素にアクセスできる。 添え字は 0 から始まる。添え字として整数のほか,整数の範囲が使える。

配列のサイズより大きな添え字で値を得ようとすると,nil が返される。 配列のサイズより大きな添え字で値をセットすると, 配列は自動的にその箇所まで拡張され,あいだには nil が補完される。

配列はブラケット [] で構築できる。

[74, 97, 115, 109, 105, 110, 101]

文字列の配列には特別な略記法がある。

%w(The great ruby, symbol of happiness, red as blood)

これは下記と同じである。

["The", "great", "ruby,", "symbol", "of", "happiness,", "red", "as", "blood"]

配列の中の値を取得または代入するにはブラケット [] で添え字付けする。 配列の長さは length メソッドで取得できる。 指定した文字列を要素間にはさんで文字列を作るには join メソッドを使う。

irb(main):001:0> a = %w(The great ruby, symbol of happiness, red as blood)
=> ["The", "great", "ruby,", "symbol", "of", "happiness,", "red", "as", "blood"]
irb(main):002:0> a[0]
=> "The"
irb(main):003:0> a[2]
=> "ruby,"
irb(main):004:0> a[0..2]
=> ["The", "great", "ruby,"]
irb(main):005:0> a[-1]
=> "blood"
irb(main):006:0> a[3..-1]
=> ["symbol", "of", "happiness,", "red", "as", "blood"]
irb(main):007:0> a[2, 4]
=> ["ruby,", "symbol", "of", "happiness,"]
irb(main):008:0> a[1, 1]
=> ["great"]
irb(main):009:0> a[1, 1] = nil
=> nil
irb(main):010:0> a
=> ["The", "ruby,", "symbol", "of", "happiness,", "red", "as", "blood"]
irb(main):011:0> a[2..4]
=> ["symbol", "of", "happiness,"]
irb(main):012:0> a[2..4] = ["happy", "symbol,"]
=> ["happy", "symbol,"]
irb(main):013:0> a
=> ["The", "ruby,", "happy", "symbol,", "red", "as", "blood"]
irb(main):014:0> a.length
=> 7
irb(main):015:0> a.join(" ")
=> "The ruby, happy symbol, red as blood"
irb(main):016:0> a.join("---")
=> "The---ruby,---happy---symbol,---red---as---blood"

a[0..2] は添え字位置 0 から 2 までの部分列を作る。 負の添え字は終わりから数える。最終要素の位置が -1 である。 a[2, 4] は添え字位置 2 からの長さ 4 の部分列を作る。 a[1, 1] = nil は添え字位置 1 から長さ 1 の部分列を削除する。 a[2..4] = anohter_array は添え字位置 2 から 4 の部分列を 別の配列 anohter_array の要素で置き換える。

他のオブジェクトと同じく,配列は「参照」 (つまり リファレンス,つまり ポインタ) として式の中で扱われる。 したがって,配列で循環構造を構成することはやさしい。 そして,そうしても文字列表現は破綻しない。

irb(main):001:0> a = [0, 1, 2, 3]
=> [0, 1, 2, 3]
irb(main):002:0> a[3] = a
=> [0, 1, 2, [...]]
irb(main):003:0> a[3][3][3]
=> [0, 1, 2, [...]]

このとき,配列 a がどんな構造になっているか,箱と矢印を使って図にしてみよう (末尾の要素から先頭へと矢印でつながっている図が書けるはずだ)。 末尾以外の要素で循環路をつくったら文字列表現はどうなるだろうか。 複数の配列を用意して,互いに相手を参照するような構造を作ったらどうなるだろうか。

こうした構造の有用性は人によっては自明ですが,そうでない場合でも, どこか頭の片隅に記憶しておきましょう。いつか役立つはずです。

push(arg)arg を配列の末尾に追加する。 pop() は配列の末尾の要素を取り除いて,その要素を戻り値とする。

unshift(arg)arg を配列の先頭に挿入する。 shift() は配列の先頭の要素を取り除いて,その要素を戻り値とする。

これら4メソッドは Perl にちなんだものです。さらに shift は Perl が Unix Shell から借りたものです。

4.7 ハッシュ (Hash)

Ruby のハッシュ (クラス名は Hash) は,Java の HashMap と同じく, ハッシュ表によって実現された任意のキーから任意の値への写像である。 ある範囲の整数から値への写像とみなせる「配列」と異なり, ハッシュに含まれるキーどうしは (ハッシュそれ自身によっては) 順序付けられていない。 例えば,キーと値のペアをハッシュに格納した順序がなんらかの形で保存されていて 後から必ずその順序でキーを取得できる,ということはない。

新しいハッシュを作成するには波括弧を使う。

{1 => 2, 3 => 4}

驚くべきことに,キーと値を単にコンマで区切ってもよい (Ruby に残存する暗黒面のいくつかと同じく,これは Perl にインスパイアされた仕様である。 手軽だが誤解を招きやすいから,実際のコーディングでこの書き方をするときは, コメントでハッシュのリテラルであることを注意すると親切であろう)

{1, 2, 3, 4}

配列と同じく,ブラケットを使って値を取得または代入する。 取得時,該当するキーがなければ,nil が返される。

h = {1 => 2, 3 => 4}
h[100] = 5
p h[3]          # 4 が印字される
p h[50]         # nil が印字される

該当するキーがないときの値を指定して陽に Hash インスタンスを構築することもできる。

h = Hash.new(-1)
p h[50]         # -1 が印字される

4.8 正規表現 (Regexp)

Ruby が Perl にインスパイアされた (暗黒面ではない) 特徴の一つは,正規表現の強力なサポートである。 多くの POSIX 機能,エスケープ列,最長一致,最短一致など, 君が正規表現に期待するもののほとんどを Ruby はサポートする。 正規表現のリテラルの構文ではスラッシュが区切り文字だが, %r と任意の区切り文字を使うこともできる。

/foo/
%r(bar)
%r|baz|

文字列と同じように式を埋め込むこともできる。

/\*#{given_word}\*/

これはローカル変数 given_word の値が "Tora" のとき, *Tora* という文字の列にマッチし, "[^a-z]" のとき,英小文字以外の1文字が * ではさまれている文字の列にマッチする。

文字列に対して正規表現をマッチするには,=~ または === を使う。 === は,正規表現がマッチすれば true を, しなければ false を返す。 =~ メソッドを使ってマッチすると, 最初に一致した正規表現に対応する文字列の添え字位置が返される。

マッチングについての情報を保持するために,Perl と同じく,いくつか特別な変数が使われる。 正規表現内のグループにマッチしたとき, 各グループに順に対応する変数 $1 から $9 を使ってその値を取り出すことができる。

4.9 範囲 (Range)

範囲 (クラス名は Range) は,先頭 begin と終端 end とその間のすべてを表す。 範囲には終端を含む場合と含まない場合がある。 含まない場合,メソッド exclude_end?true を返す。 範囲は,普通は数の値域を表すために使われるが,文字列や他の型の範囲も可能である。 比較演算子 <=> (左辺が右辺と比べ 小さい/等しい/大きい 時それぞれ -1/0/1 を返す) を適用でき, succ メソッド (successor, 後続者) が用意されていればよい。

1..9            # 終端を含む (inclusive): 1, 2, ..., 8, 9
1...100         # 終端を含まない (exclusive): 1, 2, ..., 98, 99
'aaa' .. 'zzz'  # 長さ 3 の英小文字の組み合わせ
irb(main):001:0> r = 1..9
=> 1..9
irb(main):002:0> r.begin
=> 1
irb(main):003:0> r.end
=> 9
irb(main):004:0> r.exclude_end?
=> false
irb(main):005:0> r.to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

範囲クラスは === 演算子をレシーバに引数が含まれるかどうかを判定するように上書き定義している。 判定は先頭と終端に対し引数を <=> で比較して行う。 したがって,例えば (1..9) === a1 <= a && a <= 9 は概ね同じ計算コストである。 これは case で有用である。

(1..9) === 0    # => false
(1..9) === 5    # => true
範囲クラスは,含まれる要素を先頭から順に与えるイテレータ each を定義することによって, 伝統的な表現の for を可能にしている。 下記は i を 1, 2, ..., 8, 9 と変化させて doend 内を繰り返す。
for i in 1..9 do
  puts i
end

4.10 NilClass, TrueClass, FalseClass

Ruby の nil は役割としては C/C++ の NULL,Java の null に相当するが,クラスのインスタンスであり,to_s などのメソッドを呼び出すこともできる。 Ruby では,nilfalse 以外はすべて真理値の真と見なされる。 明示的な真の値として true もある。 truefalse もそれぞれクラスのインスタンスである。

これらはそれぞれ固有のクラスに属する。

irb(main):001:0> nil.class
=> NilClass
irb(main):002:0> true.class
=> TrueClass
irb(main):003:0> false.class
=> FalseClass


戻る


Copyright (c) 2007, 2008 Oki Software Co., Ltd.