web-dev-qa-db-ja.com

Rubyで文字列の代わりにシンボルを使用する場合

スクリプトに同じ文字列のインスタンスが少なくとも2つある場合、代わりにシンボルを使用する必要がありますか?

93
Alan Coromano

TL; DR

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

完全な答え

動的に生成される識別子にそれらを使用しない唯一の理由は、メモリの問題です。

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

シンボルと文字列の実装にはいくつかの違いがあります。シンボルについて最も重要なことは、シンボルがimmutableであることです。これは、値が変更されないことを意味します。このため、シンボルは文字列よりも速くインスタンス化され、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.
_

Ruby 2.2より前のバージョンでは、シンボルがインスタンス化されると、このメモリは二度と解放されなくなります。唯一の方法メモリを解放するには、アプリケーションを再起動する必要がありますので、誤って使用すると、シンボルもメモリリークの主な原因になります。メモリリークを生成する最も簡単な方法は、ユーザー入力データに対してメソッド_to_sym_変更すると、メモリの新しい部分がソフトウェアインスタンスで永久に使用されますRuby 2.2は、動的に生成されたシンボルを解放する symbolガベージコレクター を導入しました。シンボルを動的に作成することによって生成されるリークは、もはや問題ではありません。

あなたの質問に答える:

アプリケーションまたはスクリプトに同じ文字列が少なくとも2つある場合、文字列の代わりにシンボルを使用する必要があるのは本当ですか?

探しているのがコードで内部的に使用される識別子である場合、シンボルを使用する必要があります。出力を印刷する場合は、文字列が複数回表示される場合でも、メモリ内の2つの異なるオブジェクトを割り当てる場合でも、文字列を使用する必要があります。

理由は次のとおりです。

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

@AlanDertによるユースケース

@AlanDert:hamlコードで%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の使用は文字列の使用よりも少し高速です。何故ですか? HAMLの実装方法に依存します。確認するには、HAMLコードを少しハックする必要がありますが、識別子の概念でシンボルを使用し続けると、アプリケーションの速度と信頼性が向上します。疑問が生じたら、ベンチマークを行い、回答を取得します。

168
fotanus

簡単に言えば、シンボルは名前であり、文字で構成されていますが、不変です。それどころか、文字列は、文字の順序付けられたコンテナであり、その内容は変更できます。

13
Boris Stitnicky
  1. Rubyシンボルは、O(1)比較を持つオブジェクトです

2つの文字列を比較するには、すべての文字を調べる必要がある可能性があります。長さNの2つのストリングの場合、これにはN + 1の比較が必要になります(コンピューター科学者は「O(N)時間」と呼びます)。

def string_comp str1, str2
  return false if str1.length != str2.length
  for i in 0...str1.length
    return false if str1[i] != str2[i]
  end
  return true
end
string_comp "foo", "foo"

しかし:fooの出現はすべて同じオブジェクトを参照するため、オブジェクトIDを調べることでシンボルを比較できます。これは、単一の比較(コンピューター科学者が「O(1)時間」と呼ぶ)で行うことができます。

def symbol_comp sym1, sym2
  sym1.object_id == sym2.object_id
end
symbol_comp :foo, :foo
  1. Rubyシンボルは自由形式の列挙のラベルです

C++では、「列挙」を使用して、関連する定数のファミリーを表すことができます。

enum BugStatus { OPEN, CLOSED };
BugStatus original_status = OPEN;
BugStatus current_status  = CLOSED;

ただし、Rubyは動的言語であるため、BugStatusタイプを宣言したり、正当な値を追跡したりする心配はありません。代わりに、列挙値をシンボルとして表します。

original_status = :open
current_status  = :closed

3.A Rubyシンボルは定数、一意の名前です

Rubyでは、文字列の内容を変更できます。

"foo"[0] = ?b # "boo"

ただし、シンボルの内容を変更することはできません。

:foo[0]  = ?b # Raises an error
  1. Rubyシンボルはキーワード引数のキーワードです

キーワード引数をRuby関数に渡すとき、シンボルを使用してキーワードを指定します。

# Build a URL for 'bug' using Rails.
url_for :controller => 'bug',
        :action => 'show',
        :id => bug.id
  1. Rubyシンボルはハッシュキーに最適です

通常、ハッシュテーブルのキーを表すためにシンボルを使用します。

options = {}
options[:auto_save]     = true
options[:show_comments] = false
8
Arun Kumar M

コードアカデミーで見つけたニース文字列とシンボルのベンチマークは次のとおりです。

require 'benchmark'

string_AZ = Hash[("a".."z").to_a.Zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.Zip((1..26).to_a)]

string_time = Benchmark.realtime do
  1000_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  1000_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

出力は次のとおりです。

String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.
5
Yurii
  • ハッシュキー識別子としてシンボルを使用する

    {key: "value"}

  • シンボルを使用すると、メソッドを異なる順序で呼び出すことができます

     def write(file :, data :, mode: "ascii")
#簡潔にするために削除
 end 
 write(data:123、file: "test.txt")
  • 凍結して文字列として保持し、メモリを節約する

    label = 'My Label'.freeze

0