Saturday, August 02, 2008

シンボルは文字列の皮を被った整数だ!

オブジェクト
Ruby空間における操作対象(オペランド)はオブジェクトである
Rubyオペレータはオブジェクトしか取り扱わない
オブジェクトはユーザによって初めてRuby空間に生み出されるが
それはクラスという設計図に従って常に構築される

ユーザは新しいクラスを設計して
それに基づいてオブジェクトを生成することもできるけれども
数字(整数)、文字列、シンボルなどのオペランドは
既にRuby設計者によって設計されたクラスFixnum, String, Symbolに属しているので
簡単にRuby空間に生み出すことができる

'Charlie'
183
:name

文字列
Ruby空間に生み出されたすべてのオブジェクトは
それぞれが固有のID(object_id)を持っていて
RubyオペレータはこのIDで個々のオブジェクトを管理する

一方ユーザはこのIDにアクセスすることはできるけれども
このIDでオブジェクトを管理することはできない
代わりにユーザは
変数(あるいは定数)という名札をオブジェクトに付けてこれを管理する

my_name = 'Charlie'

これが文字列のオブジェクトに名札を付ける方法だ
以降ユーザはmy_nameを呼ぶことによって
Charlieオブジェクトにアクセスできるようになる

me = my_name

このようにして
複数の名札を付けることもできる

世に同名の人が複数存在するように
同名の文字列オブジェクトも複数存在しうる

his_name = 'Charlie'

これをRuby空間に新たに生成したとき
my_nameとhis_nameは同名のオブジェクトを指しているけれども
それらは異なるIDを備えた異なるオブジェクトである

my_name.object_id  # => 8848520
his_name.object_id  # => 8827340

だから僕が逆さの国'napaJ'に引っ越して
名前が変わっても彼の名前はそのままだ

my_name.reverse! # => "eilrahC"
me                        # => "eilrahC"
his_name              # => "Charlie"

変数はユーザが文字列オブジェクトを管理するための唯一の方法だ
だから対象の文字列オブジェクトが名札である変数を失うと
ユーザはそれを見失いもう管理できなくなる

his_name = 'Fox'

このようにCharlieに割り当てた変数his_nameをFoxに割り当て直すと
元のCharlieオブジェクトからはhis_nameの名札が外れる
結果ユーザはCharlieオブジェクトに対するアクセス手段を失う
Charlieオブジェクトのその後を知っているのはRubyオペレータのみとなり
そのようなオブジェクトは彼が後に破棄する

数字
整数のオブジェクトに名札を付ける方法も
文字列の場合と変わらない

my_number = 183
his_number = 183

だけど文字列の場合と異なって
これらのオブジェクトのIDは同じになる(Fixnumのみ)

my_number.object_id  # => 367
his_number.object_id  # => 367

つまりmy_numberもhis_numberも
一つの183という整数オブジェクトを指している
これはつまり一つのRuby空間には
整数183というオブジェクトは一つしか存在しないということを意味している

数字は文字列のような個性を持たず
それ自体が変化することは期待されない
だからIDが同じでも問題は生じないということなんだろう

そうすると本来整数には名札を付ける必要すらない
整数に対するオブジェクトは一意であり
ユーザは文字列の場合とは異なって
名札がなくても希望するオブジェクトにアクセスできるからだ
つまりユーザにとって
整数自体がそのオブジェクトの名札の役割を担う

Rubyでは数字にも名札を付けることができるけれども
これは対象オブジェクトの管理のためではなく
もっぱら代数演算の結果を格納する容器としての役割を担っている

a = 10
b = a * 3

シンボル
シンボルは文字列と一対一で対応する記号である
文字列'Charlie'に対応するシンボルは:Charlieとなる
文字列のところで説明したように

my_name = 'Charlie'
his_name = 'Charlie'

とした場合
2つの異なるオブジェクトがRuby空間に生成される

my_name.object_id  # => 8848520
his_name.object_id  # => 8827340

これらのシンボルは以下により得られる

my_name.intern # => :Charlie
his_name.intern # => :Charlie

当然に異なるオブジェクトのシンボルは
異なるオブジェクトであることが期待される
しかしそうはならない

my_name.intern.object_id # => 314378
his_name.intern.object_id # => 314378

つまり同一記号のシンボルは同じオブジェクトである
これはつまり一つのRuby空間には無数の"Charlie"が存在しうるけれども
これに対応する:Charlieというシンボルオブジェクトは
ただ一つしか存在しないということを意味している

そうこれはまるで整数だ

整数と同様にシンボルには名札を付ける必要はない
シンボル記号に対するオブジェクトは一意であり
ユーザは名札がなくても希望するオブジェクトにアクセスできる
つまりシンボル記号自体がそのオブジェクトの名札の役割を担う

ここまで来ればシンボルの正体ははっきりする
文字列のような顔をして
数字のようにそれ自体が名札として機能する

そうシンボルとは…

文字列の皮を被った整数だったんだ!

シンボルの出番
Rubyには複数の関連するオブジェクトを
ユーザがまとめて管理できるようにするオブジェクトがある
配列とハッシュである

our_name = [ 'Charlie', 'Fox', 'Henry' ]

これでRuby空間に3つの文字列オブジェクトを管理する1つの配列オブジェクトが生成され
それにour_nameの名札が付けられる
配列で管理されるオブジェクトへのアクセスは
その位置を表す数字で行なえる

our_name[1] # => "Fox"

でも数字には個性がないので
1と"Fox"との間にはその位置以上の関連性はない
できればもっと関連性を持たせて意味付けをしたい
そのような場合シンボルが使える

our_name = { :my_name => 'Charlie', :his_name => :my_nephew => 'Henry' }

これでRuby空間に3つの文字列オブジェクトを管理する1つのハッシュオブジェクトが生成され
それにour_nameの名札がつけられる
ハッシュで管理されるオブジェクトへのアクセスは
そのキー値を使って行う

our_name[:his_name] # => "Fox"

なおRuby空間では:his_nameは"his_name"に対応しているが
それらは別のオブジェクトなので

our_name["his_name"] # => nil

となる
Rails空間では事情が異なるようだ

シンボルの特性をまとめてみよう
(1)文字列と一対一に対応した記号である
(2)同一記号のシンボルはRuby空間に唯一つ存在する(整数と同様、文字列と相違)
(3)シンボル記号自体が意味付けを表象できる(文字列と同様、整数と相違)

(2)(3)の特性を考えれば
そのオブジェクトの変更が予定されない場合
無駄なオブジェクトが生成されないシンボルは
速度の点で文字列オブジェクトよりも有利であり
またその記号の可読性を高めたい場合
整数オブジェクトよりも有利である
(1)の特性を活用したシンボルの使い方を僕は知らない

No comments: