2006-12-01
■ [Ruby/SDL] C言語のenumの代わりには、StringじゃなくてSymbolを使おう
要するに、
case key when "UP" then @y -= 1 when "DOWN" then @y += 1 when "RIGHT" then @x += 1 when "LEFT" then @x -= 1 end
じゃなくて
case key when :up then @y -= 1 when :down then @y += 1 when :right then @x += 1 when :left then @x -= 1 end
のように書こう、って話を少し。
この手の「プログラム内部でしか使わない文字列」は、以下のような理由でSymbolを使ったほうが良いです。
Symbolとは (概念的に)
シンボルはLisp由来の機能で、 リファレンスでは「任意の文字列と一対一に対応するオブジェクトです」と定義されています。
概念的には、何かを「指し示す」ためのものがSymbolだと思えば良さそうです。 例えば 「attr_accessor :x」の「:x」 のように「インスタンス変数@xを指し示す」とか、上の例のように「上下左右の方向を指し示す」とか。 ゲームプログラミングでは、C言語のenumのように「何かの種類」を指し示す時に使うことが多いと思います。
Symbolとは (実際的に)
実装上は、シンボルの実体は数値になっています。 また、同じ文字列のシンボルは実体を共有します。 つまり、文字列一つに対し一つの数値が対応します。*1
シンボルは、to_sを呼ぶことで文字列に変換することができます(:hoge.to_s は "hoge" を返す)。
シンボル名には大文字も小文字も使えますが、小文字が使われることが多いようです(また、:hoge と :HOGE は区別される)。
Symbolを使うメリットを以下に挙げます。
(1) シンボルの方が軽い
Symbolは数値で実装されているので、
- オブジェクト生成が速い
- 文字列よりも高速に比較できる
といった特徴があります。特に、シンボルは最初の :hoge のときのみインスタンスが作られるので非常に高速です。 (文字列はリテラルが出てくるごとにString.newが呼ばれる)。
(2) コードが読みやすくなる
シンボルは文字列に比べて使い道がはっきりしているので、可読性が向上します。
(3) 無用なバグを避けることができる
シンボルはimmutableなので、Stringのように内容があとから書き換えられる心配がありません。
(4) Symbolの方がタイプ数が少ない
文字列だと「"」が2個要る(しかもシフトも必要)のに対し、Symbolは「:」1個なので打つのが楽です(^^;
参考
*1 といってもmd5みたいなものではなく、使われたシンボルから順に番号を振っていく感じ
■ [ruby] immutableについての補足
immutableとは、「内容が不変である」という意味です。
例えばStringはimmutableではないので、
# 銭形警部 keibu = "Zenigata Keibu" p keibu #=> "Zenigata Keibu" # だと思ったら…(べりべり) keibu.gsub!(/Zenigata/, "Lupin") keibu.gsub!(/Keibu/, "the 3rd") # ばかもーん、そいつがルパンだ!追えーっ! p keibu #=> "Lupin the 3rd"
という感じで、「同じインスタンスなのにいつの間にか内容が入れ替わっている」ということが起こり得ます。
一方Symbolにはgsub!のような内容を変えてしまうメソッド(「破壊的なメソッド」と呼ぶ)は存在しないので、 このようなことは起こりません。
Rubyの組み込みクラスの中では、シンボルや数値がimmutableです。配列やハッシュはmutableです。