LoginSignup
14
8

More than 5 years have passed since last update.

RubyのHashクラスにおけるキーの書きかたいろいろ

Posted at

使用環境

  • Ruby: 2.5.0rc1

キーの書き方

シンボルでキーを定義

キー名を書き、コロン : をどちらかに隣接させる。

phases = {first: "The terror of death }
=> {:first=>"The terror of death"}

コロンを左側に置く場合はファットアロー => でキーと値を繋げる。

phases = { :third => "The Propagation" }
=> {:third=>"The Propagation"}

phases = { :third "The Propagation" }
syntax error, unexpected '}', expecting end-of-input
{ :third "The Propagation" }
                           ^

キーから値を参照する時は左側にコロンを記述する。

phases[:first]
=> "The terror of death"

文字列でキーを定義

クオテーション ' or " でキーを囲む

phases = { "first" => "The terror of death" }
=> {"first"=>"The terror of death"}

ただし、ファットアローでキーと値を繋げていないとシンボルとして解釈されてしまう。

phases = { "second": "The mirage of deceit" }
=> {:second=>"The mirage of deceit"}

参照する時は文字列のようにクオテーションでキーを囲む。

phases["first"]
=> "The terror of death"

どうして2通り存在するのか?

上記の通りキーの書き方は大きく分けて2種類あるのですが、特定のキーに紐づけてある値は同じ形式で書かないと参照できない仕組みになっている。

phases = { fourth: "The prophet" }

phases[:fourth]
=> "The prophet"
phases["fourth"]
=> nil

このことからRubyでは2種類のキーを別物として扱っていることがわかるけど、なぜこんなややこしい仕様になっているのか?
(キー定義は2種類あるけど、参照するときはどちらの書き方でも良いのでは?)

Ver 1.8までは文字列で統一されていた

The History of Ruby中でこのような記述があった。

Ruby 1.9 (development version) was released in December, 2007, then stabilized 4 years later (2011) as Ruby 1.9.3. Ruby 1.9.3 was the production version of 1.9.2. These versions brought new changes to the language, such as:
- New hash syntax ({ foo: 'bar' })

Hashクラスのキーにシンボル形式が追加されたのはRuby1.9からで、それまでは文字列による定義のみとの事。1

シンボルは処理速度を向上させるために誕生した

シンボルの特徴についてはこれらの記事が参考になった。

Rubyの文字列とシンボルの違いをキッチリ説明できる人になりたい

オブジェクト指向スクリプト言語Rubyリファレンスマニュアル >> class Symbol

シンボルの特徴は

シンボルは、ソース上では文字列のように見え、内部では整数として扱われる、両者を仲立ちするような存在です。

これにより、

  • 新しく文字列を生成しない分やや効率がよく、比較も高速。
  • 文字の意味がはっきりするのでコードが読みやすくなる
  • immutableなので内容を書き換えられる心配がない

との事。

試しにbenchmarkを使ってHashを複数回呼び出すのにかかる時間を簡単に計測してみた。

require 'benchmark'

n = 100000000

def call_symbol
  return { first: "The terror of death" }
end

def call_string
  return { "first" => "The terror of death" }
end

Benchmark.bm(7) do |x|
  x.report("symbol:") { n.times do; call_symbol; end }
  x.report("string:") { n.times do; call_string; end }
end

シンボルの方が約10秒早く処理を終えている。n=10000000でも既に約1秒の差が出ているため、処理回数を増やしていくと更に広がっていくことがわかる。

              user     system      total        real
symbol:  37.926663   0.007926  37.934589 ( 38.032322)
string:  40.249538   0.004086  40.253624 ( 40.363708)

省メモリ、処理速度向上の面からシンボルが主流となって使われている(はず)だが、現在でも文字列形式が採用されているのは、Rubyをバージョンアップする事で今までのコードに支障をきたすという副作用を防ぐ目的があるのだろうか。

キーの種類で異なるメリットいろいろ

シンボルは書き方がシンプル

シンボルで書く最大のメリットは可読性が関わってる。
純粋にファットアローを用いて文字列でキー定義をするよりも見た目がスッキリしているのがわかる。

phases = { "first" => "The terror of death", "second" => "The mirage of deceit"  }
phases = { first: "The terror of death", second: "The mirage of deceit" }

一行で書くと前者はどこがキーでどこが値を示しているか認識するのに時間がかかりると思う。

文字列は柔軟なキーの定義ができる

たとえば、キーに数字や記号を含みたい場合は文字列を使う事で実現できる。

# シンボルで書くとSyntax Errorが発生
phases = { 5th: "The machinator" }
SyntaxError: (irb):1: syntax error, unexpected tIDENTIFIER, expecting =>
phases = { 5th: "The machinator" }
            ^

# 文字列であれば一文字目が数字でも問題なく定義できる
phases = { "6th" => "The temptress" }
=> {"6th"=>"The temptress"}

注:数字や一部の記号の場合、一文字目でなければシンボルでもキーの記述は可能である。 2

phases = { eighth2nd: "The rebirth" }
=> {:eighth2nd=>"The rebirth"}

また、変数に格納した値を使いたい時も文字列は便利である。

first = "skeith"

# 変数を使うときはダブルクオーテーションで囲むこと
phases = { "#{first}" => "The terror of death" }
=> {"skeith"=>"The terror of death"}

動的にキーを作成するメソッドを作るときなどに使えそう。

ハイブリッドな書き方

シンボルと文字列を組み合わせた書き方も存在する。

eighth = "2nd corbenik"
phases = { "#{eighth}": "The rebirth" }
=> {:"2nd corbenik"=>"The rebirth"}

これにより、上記2点のメリットを保ったキーの定義が可能である。

おしまい

他にシンボルと文字列それぞれ固有のメリットがあったら教えて頂けると幸いです。


  1. Ruby1.8をインストールできず、実際にシンボルが使えないかについては未検証。 

  2. ただし+、-などの単項演算子はSyntax Errorを発生させてしまう為利用できない。 

14
8
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8