もくじ
Railsに挑戦してみて、なんだこれ?と思ったものの1つが、コロンを使った書き方。
:nameとか、:titleみたいな接頭辞としてコロンを使うやつ。
これはシンボルというRubyの構文の1つらしいので、その要点をまとめてみる。
スポンサーリンク
シンボルとは文字列を通し番号で管理しているもの
シンボルって何なのかと言うと、ほぼ文字列(String)と同じ。何も難しく考えることはない。
普通の文字列とどう違うかと言うと、文字列に通し番号を割り振ることで、裏ではただの番号(整数)として管理してるってだけ。
スポンサーリンク
Hashのキーとして使うシンボル
シンボルを理解する上で一番分かりやすいのが、Hashのキーとしてシンボルを使う場合。
1 2 3 4 5 6 7 8 | data = {:name=>"Nobuo", :location=>"Osaka", :message=>"よろしくお願いします"} p data[:name] p data[:location] p data[:message] #=> "Nobuo" #=> "Osaka" #=> "よろしくお願いします" |
Hashのキーっていうのは、まさに識別の為の文字列であって、極論を言えば何でもいい。ただ人間がソースコードを読んだ時に分かりやすいように文字列を使っているだけであって、文字列自体にはプログラム上の意味はない。
1 2 3 4 | data = ["Nobuo", "Osaka", "よろしくお願いします"] p data[0] p data[1] p data[2] |
↑極論を言うと、これでも同様の結果が得られるし、書き手が配列の意味的順番を覚えていれば何の問題もない。
しかしながら、もちろん全ての順番を覚えておくのは大変だし、順番による指定の仕方は全くもって本質的ではない。そもそも順番ではなく項目(キー)に依存する配列を定義する為に、Hashという仕組みがあるのであり、しかしながらキーには、プログラム上は文字列としての機能は必要ない。
繰り返しになるが、Hashのキーと言うのは人間がソースコードを読んだ時にその意味を汲み取れるかどうかが本質であって、実際はただの識別番号で良い。シンボルというのはまさにこういう場合の為の機能と言えると思う。
逆に、値である"Nobuo"、"Osaka"、"よろしくお願いします"といった文字列は画面に表示することもあるだろうし、その文字列自体が意味を持っていると言える(地名に関しては、現実にも郵便番号等で管理されているように、ある意味、シンボル的な管理のされ方の方が理想的と言えるかも)。
ただし、こういう使い方は出来ないので注意。↓
1 2 3 4 5 6 7 8 | data = {"name"=>"Nobuo", "location"=>"Osaka", "message"=>"よろしくお願いします"} p data[:name] p data[:location] p data[:message] #=> nil #=> nil #=> nil |
定義する時点で、シンボルを使うからこそそれと認識できる。
なお、シンボルは日本語でも大丈夫。まあでも英語の方がいいかも。
1 2 3 4 5 6 7 8 | data = {:名前=>"Nobuo", :住所=>"Osaka", :メッセージ=>"よろしくお願いします"} p data[:名前] p data[:住所] p data[:メッセージ] #=> "Nobuo" #=> "Osaka" #=> "よろしくお願いします" |
ちなみに、キーをシンボルにしたHashを定義したい場合、こういう書き方もできる。
1 2 3 4 5 6 7 8 | data = {name: "Nobuo", location: "Osaka", message: "よろしくお願いします"} p data[:name] p data[:location] p data[:message] #=> "Nobuo" #=> "Osaka" #=> "よろしくお願いします" |
コロンが前にあったり後ろにあったりして、ややこしいかも知れないが、シンボルの仕組みさえ分かればこっちの方が、見やすくて良いと思う。
シンボルは文字列のようで数値のようなもの
Rubyでは文字列も数値もオブジェクトであるのと同様、シンボルもオブジェクトである。
文字列オブジェクトは、同じ文字列を持つオブジェクトであっても別々に作った場合は、もちろん別オブジェクト。
1 2 3 4 5 | p "Nobuo".object_id p "Nobuo".object_id #=> 70132529609740 #=> 70132529609640 |
↑全く同じNobuoという文字列だが、別のオブジェクトであることが分かる(オブジェクトIDが微妙に違うのを見落とさないように)。
また破壊的メソッドによって文字列を書き換えた場合は、文字列(値)は変わっても、オブジェクトIDはそのままである。
1 2 3 4 5 6 7 8 9 10 11 12 | name = "Nobuo" p name p name.object_id p name.upcase! p name.object_id #=> "Nobuo" #=> 70346330659980 #=> "NOBUO" #=> 70346330659980 |
つまり文字列の場合は、同じオブジェクトだからと言って同じ文字列を指すとは限らないし、別のオブジェクトだからと言って別の文字列を持つとも限らない。
それに対して、数値の場合は、同じ値なら、どこにあろうといつ出てこようと絶対に同じオブジェクトである。
1 2 3 4 5 | p 123.object_id p 123.object_id #=> 247 #=> 247 |
123という数値のオブジェクトIDが247になるのがどういう仕組なのか知らないが、123という数値は全て同じオブジェクトを参照している。ID247のオブジェクト以外に123という数値を表すオブジェクトは無い。
シンボルは、どうかと言うと、
1 2 3 4 5 | p :name.object_id p :name.object_id #=> 67368 #=> 67368 |
同じ表記のシンボルなら、同じオブジェクトであることが分かる。数値と同じく、値(文字列)が同じなら絶対に同じオブジェクトである。
つまり、シンボルというのは、見た目は文字列によって識別するものだが、内部的には数値のような振る舞いをしているものだと言える。
メソッドや変数などの名称を指示するためのシンボル
クラスを定義する際にインスタンス変数及びアクセサメソッドを一発で作ってしまう書き方がある。
1 2 3 | class Person attr_accessor :name end |
2行目のattr_accessorというメソッドがそれに当たる。ここでは引数に:nameというシンボルを渡している。
もしこれをattr_accessorメソッドを使わずに書くなら以下のようになる。
1 2 3 4 5 6 7 8 9 | class Person def name= name @name = name end def name @name end end |
attr_accessorの引数に:nameというシンボルを渡すことによって、
- @name というインスタンス変数
- name= というセッターメソッド
- name というゲッターメソッド
を同時に定義していることになる。
このように、attr_accessorメソッドは、引数に渡された名称(文字列)を基にインスタンス変数およびそのアクセサメソッドを作っている。
ちなみにattr_accessorメソッドの引数は、シンボルではなく普通の文字列でも問題ない。
1 2 3 | class Person attr_accessor "name" end |
シンボルというのは、変数やメソッドの中身ではなく、あくまで名称を表しているというのが分かると思う。
メソッドを指定する為のシンボル
Railsのコントローラーにて使えるbefore_actionというメソッドがある。
before_actionメソッドは、各アクションに共通する処理を行いたい時に使うメソッド。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class MembersController < ApplicationController before_action :set_text def index end def new end private def set_text @text = "Hello" end end |
ここでbefore_actionメソッドの引数に渡しているのが、:set_textというシンボル。
こう書くことで、各アクションを実行する直前に、set_textという名のメソッドを実行する。
メソッドを指定する為にシンボルを使っているのが分かると思う。
この場合、メソッドの中身は関係ない。あくまでどのメソッドなのかを指定しているだけ。
シンボルは内部で自動的に作られている
なぜ、メソッド名をシンボルで指定することができるのか?
それは、メソッドや変数、またはクラスなんかを定義すると、内部的にそれらを指定するためのシンボルオブジェクトが勝手に作られるようになっているから。
Symbol.all_symbolsメソッドで、作成済みのシンボルを全て確認することができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Person attr_accessor :name end def hoge end fuga = 1 puts Symbol.all_symbols # デフォルトで作られている大量のシンボルがどばーーーっと出力され、その最後の方に、 # :Person, :hoge, :fuga # も確かにあるので暇な人は確かめてみてほしい |
※なぜかattr_accesserメソッドを使って作ったメソッド、変数(ここでは@name変数, nameメソッド, name=メソッド)に関してはシンボルが作られない。ちょっと謎仕様。
慣れると絶対、読みやすいし使いやすいと思うので、Rubyでは積極的にシンボルを使おう。