4. 主なクラス
Ruby はさまざまな目的のために多くのクラスを用意している。 一般にクラスのインスタンスは,メタクラスのメソッド new によって構築されるが, 文字列など,特定のクラスのインスタンスは,特別な構文で構築することもできる。 ここでは主なクラスを紹介する。クラス名は定数だから大文字で始まる。
4.1 Object
Object はすべてのクラスの基底クラスである。
特に禁止しない限り Ruby のクラスは開いており,いつでも拡張可能だが, とりわけ 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")
トップレベルでこれらのメソッドと同じ名前の関数を適当に定義してみよう。
何が起こるだろうか。
メタクラスのインスタンスとしての Object を freeze させてみよう
(Object.freeze を実行すればよい)。
何が起こるだろうか。
クラスを定義するときは,たいていの場合,private インスタンス・メソッド initialize を適切に上書きする必要がある。
- initialize()
-
C++/Java のコンストラクタに相当する。
メタクラスのメソッド new は,クラスの新しいインスタンスを構築するとき,
新インスタンスをレシーバとして initialize メソッドを呼び出す。
new メソッドは任意の引数をとる。new メソッドの引数がそのまま initialize メソッドに渡される。 Object での initialize の実装は,引数をとらず。何もしない。 派生クラスには,適切な仮引数宣言と適切なメソッド本体で initialize を上書き定義することが期待されている。
派生クラスの initialize メソッドの本体は,基底クラスとしての初期化を行うために super を呼び出すのが普通である。 特に C++/Java の経験者は下記の点に注意する必要がある。
- 陽に super を呼び出さない限り,基底クラスの initialize は実行されない。 C++/Java と異なり,基底クラスの無引数コンストラクタの自動的な呼出しのようなものはない。
- 他のメソッドと同じく initialize メソッドは継承される。 したがって,派生クラスでインスタンス変数を追加しないときは,わざわざ initialize を定義しなくてよいことが多い。
もしも下記の例を 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>
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"
[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 独特の変換指定 %p は inspect による文字列表現である。
qty = 100
qlty = "gold"
puts "%d %p coins to every finalist" % [qty, qlty] # 100 "gold" coins… と印字
4.3 シンボル (Symbol)
シンボル (クラス名は Symbol) は文字列と似ているが,書換え不能であり, どのシンボルも同じ名前をもつ他のシンボルと equal? メソッドの判定で等しい。 つまり,シンボルは整数のように高速に同一性を判定できる。 アイデンティティというたかだか1ワード程度の整数を比較すればよいからである。
典型的には,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.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) === a と 1 <= a && a <= 9
は概ね同じ計算コストである。
これは case 文 で有用である。
(1..9) === 0 # => false (1..9) === 5 # => true範囲クラスは,含まれる要素を先頭から順に与えるイテレータ each を定義することによって, 伝統的な表現の for 文 を可能にしている。 下記は i を 1, 2, ..., 8, 9 と変化させて do … end 内を繰り返す。
for i in 1..9 do puts i end
4.10 NilClass, TrueClass, FalseClass
Ruby の nil は役割としては C/C++ の NULL,Java の null に相当するが,クラスのインスタンスであり,to_s などのメソッドを呼び出すこともできる。 Ruby では,nil と false 以外はすべて真理値の真と見なされる。 明示的な真の値として true もある。 true,false もそれぞれクラスのインスタンスである。
これらはそれぞれ固有のクラスに属する。
irb(main):001:0> nil.class => NilClass irb(main):002:0> true.class => TrueClass irb(main):003:0> false.class => FalseClass