1. ホーム
  2. ruby

[解決済み] Rubyで文字列の代わりにシンボルを使用するのはいつ?

2022-11-25 17:05:10

質問

スクリプト内に同じ文字列が2つ以上ある場合、シンボルを使用したほうがよいでしょうか。

どのように解決するのですか?

TL;DR

単純な経験則では、内部識別子が必要なときは常にシンボルを使用することです。Ruby < 2.2 では、メモリリークを避けるために、シンボルは動的に生成されないときだけ使用します。

完全な答え

動的に生成される識別子に使用しない唯一の理由は、メモリに関する懸念です。

多くのプログラミング言語にはシンボルがなく、文字列のみであるため、コードの中で文字列も識別子として使用されるため、この質問は非常によくあることです。あなたが心配すべきなのは、どのシンボルが であることを意味します。 だけでなく を使用する必要がある場合 . 記号は識別するためのものです。この哲学に従えば、物事を正しく行うことができる可能性があります。

シンボルと文字列の実装には、いくつかの違いがあります。シンボルについて最も重要なことは、シンボルは 不変 . これは、決して値が変更されないことを意味します。このため、シンボルは文字列よりも高速にインスタンス化され、2つのシンボルを比較するようないくつかの操作も高速になります。

シンボルが不変であるということは、Rubyがシンボルを参照するたびに同じオブジェクトを使用し、メモリを節約することを可能にします。そのため、インタープリタが :my_key を読むたびに、それを再びインスタンス化する代わりにメモリから取り出すことができるのです。これは、毎回新しい文字列を初期化するよりも低コストです。

すでにインスタンス化されているすべてのシンボルのリストを得るには、コマンド Symbol.all_symbols :

symbols_count = Symbol.all_symbols.count # all_symbols is an array with all 
                                         # instantiated symbols. 
a = :one
puts a.object_id
# prints 167778 

a = :two
puts a.object_id
# prints 167858

a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!

puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.

Ruby2.2以前のバージョンでは、シンボルがインスタンス化されると、このメモリは 二度と解放されません。 . メモリを解放する唯一の方法は、アプリケーションを再起動することです。ですから、シンボルも使い方を誤るとメモリリークの大きな原因になります。メモリリークを発生させる最も簡単な方法は、メソッド to_sym を使うことです。このデータは常に変更されるため、ソフトウェアインスタンスではメモリの新しい部分が永遠に使われ続けることになります。Ruby 2.2 では シンボルガベージコレクタ が導入され、動的に生成されたシンボルを解放するようになったので、シンボルを動的に生成することで発生するメモリリークは気にならなくなりました。

質問にお答えします。

アプリケーションやスクリプトの中に同じ文字列が2つ以上ある場合、文字列の代わりにシンボルを使わなければならないというのは本当でしょうか?

もしあなたが探しているものが、コードの内部で使用される識別子であれば、シンボルを使用すべきです。出力を印刷するのであれば、たとえそれが複数回表示され、メモリ内に2つの異なるオブジェクトが割り当てられていたとしても、文字列を使用すべきなのです。

以下はその理由です。

  1. シンボルは文字列にキャストされるため、シンボルの印刷は文字列の印刷よりも遅くなります。
  2. シンボルは決して解放されないので、多くの異なるシンボルを持つことは、アプリケーションの全体的なメモリ使用量を増加させます。そして、コードからすべての文字列を同時に使用することはありません。

AlanDert による使用例

@AlanDert: %input{type: :checkbox} のようなものを何度も使用する場合。のようなものを何度も使う場合、チェックボックスとして何を使うべきでしょうか?

私:はい。

@AlanDert: でも、htmlページでシンボルを印刷するには、文字列に変換されるはずですよね。

入力のタイプとは何ですか?使用したい入力のタイプの識別子か、ユーザーに見せたい何かですか?

確かにそれは HTML コードになるのですが、あなたがそのコードを書いている時点では、識別子であることを意味しているのです。そのため、コード内で何度も繰り返し使用され、常に同じ文字列が識別子として使用されるため、メモリリークが発生することはありません。

それはそうと、文字列の方が速いかどうか、データで評価してみませんか?

これはそのために作った簡単なベンチマークです。

require 'benchmark'
require 'haml'

str = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: "checkbox"}').render
  end
end.total

sym = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: :checkbox}').render
  end
end.total

puts "String: " + str.to_s
puts "Symbol: " + sym.to_s

3つの出力

# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68

というわけで、smbolsを使う方がstringを使うよりも実は少し速いのです。なぜでしょうか?それはHAMLの実装方法によるものです。HAMLのコードを少しハックしてみないとわかりませんが、識別子の概念でシンボルを使い続ければ、アプリケーションはより速く、より信頼性の高いものになるでしょう。疑問が湧いたら、ベンチマークして答えを出しましょう。