トップ «前の日記(2006-11-28) 最新 次の日記(2006-12-02)» 編集

Route 477



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です。