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

ブログを下記に移転しました。デザイン変更により移転先では記事が一層読みやすくなっていますので、よろしければ移動をお願い致します。

Rubyのシンボルは文字列の皮を被った整数だ! : melborne.github.com

                                                                                • -


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空間では事情が異なるようだ


シンボルの特性をまとめてみよう

  1. 文字列と一対一に対応した記号である(文字列オブジェクトとシンボルオブジェクトは多対一の関係になる)
  2. 同一記号のシンボルはRuby空間に唯一つ存在する(整数と同様、文字列と相違)
  3. シンボル記号自体が意味付けを表象できる(文字列と同様、整数と相違)


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


関連記事:
Rubyのブロックはメソッドに対するメソッドのMix-inだ! - hp12c
Rubyのyieldは羊の皮を被ったevalだ! - hp12c
Rubyのクラスはオブジェクトの母、モジュールはベビーシッター - hp12c


初めてのRuby

初めてのRuby