Rubyのシンボルは文字列の皮を被った整数だ!
Yugui著「初めてのRuby」を読んでいる。自分はこの本の想定する対象読者ではない。この本の対象読者は他言語プログラマだ。自分はRubyしか知らない、Rubyのこともまだ少ししか知らないアマチュアプログラマだ。
けれども、この本の内容は自分にとって極めて有益だ。初学者向けにありがちな方便としての「ウソ」がない。ちゃんと理解が書かれている。読者を事実に導こうとする努力がある。大見出しこそ他書と差はないが一歩小見出しに入れば、他ではお目にかかれないような表題が満載で、その内容の多くが知ってはいるけど正しく理解していなかったものばかりだ。
動く疑似コード、DSL、処理系と実行環境、実行モデル。shebang、リソース管理、コールバック、添字代入の裏側。Enumerator、型と自動変換、バックラッシュ記法、Unicode文字。バッククォート文字列、文字リテラル、シンボルの性質と用途。マルチバイト文字列、ファイルのモード、標準入出力、ARGF。ダックタイピング、StringIO、エンコーディング(5章まで)
また文章も読みやすく、タイプミスを含めた間違いも極端に少ないように思う。著者に信頼をおいて読み進めることができる。著者の力量を感じさせる。残りの章を読むのがとても楽しみだ。
「初めてのRuby」を読んでいて、シンボルのことが理解できた気がするので、自分の理解を書いてみることにする。
オブジェクト
Ruby空間における操作対象(オペランド)はオブジェクトである。Rubyオペレータはオブジェクトしか取り扱わない。オブジェクトはユーザによって初めてRuby空間に生み出されるが、それはクラスという設計図に従って常に構築される。
ユーザは新しいクラスを設計して、それに基づいてオブジェクトを生成することもできるけれども、文字列、数字(整数)、シンボルなどのオペランドは、既にRuby設計者によって設計されたクラスString、Fixnum、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は同じになる。
my_number.object_id # => 367
his_number.object_id # => 367
(Fixnumのみ,Bignum,Floatは別IDとなる)
つまり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 => 'Fox', :my_nephew => 'Henry' }
これでRuby空間に3つの文字列オブジェクトを管理する、1つのハッシュオブジェクトが生成され、それにour_nameの名札がつけられる。ハッシュで管理されるオブジェクトへのアクセスは、そのキー値を使って行う。
our_name[:his_name] # => "Fox"
なおRuby空間では:his_nameは”his_name”に対応しているが、それらは別のオブジェクトなので、
our_name["his_name"] # => nil
となる。Rails空間では事情が異なるようだ。
シンボルの特性をまとめてみよう。
- 文字列と一対一に対応した記号である(文字列オブジェクトとシンボルオブジェクトは多対一の関係になる)
- 同一記号のシンボルはRuby空間に唯一つ存在する(整数と同様、文字列と相違)
- シンボル記号自体が意味付けを表象できる(文字列と同様、整数と相違)
これらの特性を考えれば、そのオブジェクトの変更が予定されない場合、無駄なオブジェクトが生成されないシンボルは、速度の点で文字列オブジェクトよりも有利であり、またその記号の可読性を高めたい場合、整数オブジェクトよりも有利である。
関連記事: Rubyのブロックはメソッドに対するメソッドのMix-inだ! Rubyのyieldは羊の皮を被ったevalだ! Rubyのクラスはオブジェクトの母、モジュールはベビーシッター
初めてのRuby by Yugui
(comment) >シンボルについて、かなりスッキリしました。「たのしいRuby」よりそちらの方がよさげですね。ありがとうございました。
soiyamさん
お役に立ててうれしいです。
blog comments powered by Disqus