Rubyでは全ての値はオブジェクトのため、一見ただの数値に見えてもそれはInteger
クラスのオブジェクトです。
そこでmethods
メソッドを使って、そのオブジェクトが所属するInteger
クラスのメソッドを見てみたいと思います。
irb(main):006:0> 123.class
=> Integer
irb(main):007:0> 123.methods
=> [:-@, :**, :<=>, :upto, :<<, :<=, :>=, :==, :chr, :===, :>>, :[], :%, :&, :inspect, :*, :+, :ord, :-, :/, :size, :succ, :<, :>, :to_int, :coerce, :to_s, :to_i, :to_f, :divmod, :to_r, :fdiv, :modulo, :remainder, :abs, :magnitude, :integer?, :floor, :ceil, :round, :truncate, :^, :odd?, :even?, :allbits?, :anybits?, :nobits?, :downto, :times, :pred, :pow, :bit_length, :digits, :numerator, :denominator, :rationalize, :gcd, :lcm, :gcdlcm, :next, :div, :|, :~, :abs2, :angle, :+@, :conjugate, :to_c, :conj, :phase, :rect, :polar, :eql?, :singleton_method_added, :i, :real?, :zero?, :nonzero?, :finite?, :infinite?, :step, :positive?, :negative?, :clone, :dup, :arg, :quo, :rectangular, :real, :imaginary, :imag, :between?, :clamp, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :instance_variable_set, :instance_variables, :protected_methods, :private_methods, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :=~, :!~, :respond_to?, :freeze, :object_id, :send, :display, :class, :nil?, :hash, :singleton_class, :then, :itself, :yield_self, :untaint, :taint, :tainted?, :trust, :untrust, :untrusted?, :singleton_methods, :frozen?, :methods, :public_methods, :equal?, :!, :instance_exec, :!=, :instance_eval, :__id__, :__send__]
このとき、メソッド名が「:(コロン)」が付いて始まっています。
これがSymbol(シンボル)なのですが、それって一体何なのでしょうか。
Symbolとは
Ruby3.0リファレンスマニュアルで調べてみました。
シンボルの実装
Rubyの内部実装では、メソッド名や変数名、定数名、クラス名などの名前を整数で管理しています。
そしてその整数をRubyのコード上で表現したものがシンボルです。
ここで「へぇ~」と思ったのが名前を整数で管理しているという点です。
なぜそんなことをしているのでしょうか。名前なんだから文字列で管理すれば良いのでは…?
答えは書いてありました。
これは名前を直接文字列として処理するよりも速度面で有利だからです。
ふむふむ、どうやらSymbolを使った方が処理が速いようです。
文字列とシンボルの速度を比較してみた
百聞は一見に如かずなので、実際にやってみたいと思います。
速度の判定にはBenchmark-ips
というRubyGemを使ってみます。
【参考】Rubyの処理速度を確認したい時に使うGem【Benchmark-ips】
# 文字列とシンボルの速度を比較するクラス
require 'benchmark/ips'
class Measure
Benchmark.ips do |x|
x.report("String"){
hash = {}
(1..1000).each do |n|
hash.store(n, "abc")
end
}
x.report("Symbol"){
hash = {}
(1..1000).each do |n|
hash.store(n, :abc)
end
}
x.compare!
end
end
Warming up --------------------------------------
String 1.027k i/100ms
Symbol 1.301k i/100ms
Calculating -------------------------------------
String 10.815k (± 3.8%) i/s - 54.431k in 5.041519s
Symbol 13.327k (± 3.5%) i/s - 67.652k in 5.082751s
Comparison:
Symbol: 13327.4 i/s
String: 10815.3 i/s - 1.23x (± 0.00) slower
確かに!リファレンスに書かれている通り、Symbolのほうが速い結果となりました。
HashオブジェクトのvalueもSymbolにしたほうがより早く処理できそうということが分かりました。