Rubyのシンボルはクセ者だった

f:id:ryoutaku_jo:20190108020736p:plain

【結論】

  • シンボルとは、文字列の見かけをした整数
  • 文字列の代わりとして扱え、整数なので素早い処理が可能
  • 但し文字列と異なる振る舞いをする事もある
  • 構造を正確に把握しないと、コード書くときに躓く

 

【目次】

 

 

【本題】

面白そうなサイトを見つけたのが始まり

 

今日、こんなサイトを見つけてしまいました。

paiza.jp

 

エンジニア向けの求人サイトなのですが、

サイト内のプログラミングの問題を解くことで、

プログラミングのスキルが評価されて、その評価で

企業にアピールが出来るという面白い仕組みのサイトです。

 

腕試しにちょうどいいと思ったのと、

問題が非常に豊富で、どれも面白そうな物ばかりだったので、

とりあえずやってみる事にしました。

 

 

とある問題で躓く

そして、何問か解く内に、良く分からない問題に遭遇します。

その問題をそのまま載せたかったのですが、

転職の評価材料となる関係上、問題や回答をネットに公開するのは、

利用規約に抵触してしまうので、躓いたポイントだけ分かる様に、

簡略化した問題を自作しました。

 

・問題

下記フルーツの名前を入力したら、

値段を表示するプログラムを作りなさい。

名前       値段

apple      200

banana   100

orange    300

 

 

私の問題へのアプローチ

 

 そして、私の初めに書いたコードがこちら

fruits = {apple: '200', banana: '100' ,orange: '300'} 
fruits_name = fruits.keys

 

input = gets.to_sym

 

fruits_name.each do |fruit|
 if fruit == input
 puts fruits[fruit]
 end
end

 

念の為、コードの処理を説明すると、下記の様な流れ 

1.フルーツの名前をKeyに、値段をvalueにした

 ハッシュオブジェクト「fruits」を生成

 (Keyにはシンボルを利用)

2.keysメソッドで「fruits」のキーを集めて配列にし、

 「fruits_name」に代入
3.getsメソッドでフルーツの名前を入力出来る様にする
 入力した文字は、ハッシュのデータ型と合う様に
 .to_symでシンボルオブジェクトに変換
4.each文で「fruits_name」に繰り返し処理を実行
5.「if fruit == input」と記述することで、
 fruits_nameの要素一つ一つと、入力した文字が一致するか判定
 一致すれば、一致したフルーツの名前をKeyにして、
 ハッシュオブジェクトから金額を取得して出力

 

そして、これが上手く機能しなかったのですが、

コードの処理を追ってみると、

「fruit == input」が ハッシュのKeyの名前を

そのままコピペしても、falseで返って来ていることが分かりました。

 

ただeach文の中で「 fruit 」「 input」をそれぞれ呼び出して

検証しても、やはり文字は同じで、データ型も同じでした。

 f:id:ryoutaku_jo:20190108015033p:plain

 

原因が分からないが問題は解けた

原因が分からないまま、手当たり次第に修正してみたところ、

Keyをシンボルから文字列に変更したところ、

正常に機能する様になりました。

fruits = {'apple' => '200', 'banana' => '100' ,'orange' => '300'}
fruits_name = fruits.keys

input = gets.chomp

fruits_name.each do |fruit|
 if fruit == input
 puts fruits[fruit]
 end
end

 

 

 これで問題自体は解けたのですが、疑問に残るのは、

「なぜシンボルオブジェクトだと、

同じ文字・データ型でも

等式でfalseが返って来たのか?」ということです。

 

 

シンボルオブジェクトについて調べた

そもそもシンボルとは何かですが、公式のリファレンスには

この様に記載されています。

docs.ruby-lang.org

 

シンボルは任意の文字列と一対一に対応するオブジェクトです。

文字列の代わりに用いることもできますが、

必ずしも文字列と同じ振る舞いをするわけではありません。

同じ内容のシンボルはかならず同一のオブジェクトです。

 

(中略)

 

Rubyの内部実装では、メソッド名や変数名、

定数名、クラス名など の`名前'を整数で管理しています。

これは名前を直接文字列として処理するよりも

速度面で有利だからです。

そしてその整数をRubyのコード上で表現したものがシンボルです。

シンボルは、ソース上では文字列のように見え、

内部では整数として扱われる、両者を仲立ちするような存在です。

名前を管理するという役割上、シンボルと文字列は一対一に対応します。 また、文字列と違い、immutable (変更不可)であり、

同値ならば必ず同一です。

 

私の中でのシンボルのそれまでの解釈は、

 

・文字列とほぼ一緒

・ハッシュの記述が楽になる

・文字列より処理が早い

というくらいだったのですが、

どうやら文字列と根本的に違う点が、原因である可能性が高そうです。

 

 

 

 

リファレンスには、これ以上の詳しい説明が無かったですが、

この辺りについては、先日購入した

「プロを目指す人のためのRuby入門」にも

記載されていたので引用します。

 

次に、シンボルは「同じシンボルであれば全く同じオブジェクトである」

という特徴があります。(中略)

まったく同じオブジェクトであるかどうかは

object_idを調べると分かります。(中略)

:apple.object_id #=> 1143388

:apple.object_id #=> 1143388

:apple.object_id #=> 1143388 

'apple'.object_id #=> 70223819213380

'apple'.object_id #=> 70223819233120

'apple'.object_id #=> 70223819227780

ご覧の通り、シンボルはすべて同じIDになりますが、

文字列は3つとも違うIDになります。

 

なるほど、同一のシンボルオブジェクトなのかは、

object_idで調べればいいのか!

 

では、初めに書いたコードと、後に書いた上手く機能したコードの、

シンボルオブジェクトのobject_idを調べて見ました。

 

・初めのコード

f:id:ryoutaku_jo:20190108011155p:plain 

 

・上手く機能したコード

f:id:ryoutaku_jo:20190108012511p:plain

 

※それぞれ、上が入力したオブジェクト、

下がハッシュから取得したオブジェクトのobject_id

 

初めに書いたコードは、互いにシンボルオブジェクトの筈なのに、

object_idが違いますね。

 

 

そして、初めに書いたコードの上のID(入力したオブジェクト)に

注目して頂きたいのですが、文字列と同じ形式のIDになってますね。

.to_symで確かにシンボルオブジェクトに変換された筈なのに、

何故でしょうか?

 

キーワードは、「整数」と「.to_sym」まで絞り込めて来ました。

 

 

というところで、

続きは明日調べます!

長過ぎるわ・・・

 

《今日の学習進捗》 

Rubyの面白そうな問題がネットに転がっていたので、

カリキュラムそっちのけで、そちらを解き続ける。

 

Vimに振り回されて、

コーティングの生産性がガクッと落ちている(3割減)

 

でも、先々の生産性向上を期待して、

Vim縛りで当分続ける。

 

 

学習開始からの期間 :31日
今日までの合計時間:281.0h
今日までに到達すべき目標時間:283.0h
目標との解離:-2.0h

 

10,000時間」まで、

残り・・・「9719時間!」