Rubyで記号を理解する方法


85

「Rubyシンボルについて」を読んだにもかかわらず、シンボルの使用に関しては、メモリ内のデータの表現にまだ混乱しています。2つが異なるオブジェクトに含まれているシンボルが同じメモリ位置に存在する場合、それらに異なる値が含まれているのはどうしてですか?同じメモリ位置に同じ値が含まれていると思っていました。

これはリンクからの引用です:

文字列とは異なり、同じ名前のシンボルは初期化され、rubyのセッション中に一度だけメモリに存在します

同じメモリ位置に含まれる値をどのように区別するのかわかりません。

この例を考えてみましょう。

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1patient2両方ともハッシュです、それは問題ありません。 :rubyただし、はシンボルです。以下を出力する場合:

patient1.each_key {|key| puts key.to_s}

では、何が出力されますか?"red"、または"programming"

ハッシュを一瞬忘れて、シンボルは値へのポインタだと思います。私が持っている質問は次のとおりです。

  • シンボルに値を割り当てることはできますか?
  • シンボルは、値を含む変数への単なるポインターですか?
  • シンボルがグローバルである場合、それはシンボルが常に1つのことを指していることを意味しますか?

1
シンボルを印刷しているため、「:ruby」が出力されます。と言うとputs patient1[:ruby]「赤」、と言うとputs patient2[:ruby]「プログラミング」と表示されます。
ロス2016

1
シンボルは値へのポインタではありません。内部的には、シンボルは単なる整数です。
akuhn 2016

回答:


62

このことを考慮:

x = :sym
y = :sym
(x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true

x = "string"
y = "string"
(x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false

したがって、シンボルオブジェクトを作成しても、その内容が同じである限り、メモリ内の同じオブジェクトを参照します。シンボルは不変オブジェクトであるため、これは問題ではありません。文字列は変更可能です。


(以下のコメントに応えて)

元の記事では、値はシンボルに格納されておらず、ハッシュに格納されています。このことを考慮:

hash1 = { "string" => "value"}
hash2 = { "string" => "value"}

これにより、メモリ内に6つのオブジェクト(4つの文字列オブジェクトと2つのハッシュオブジェクト)が作成されます。

hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}

これにより、メモリ内に5つのオブジェクト(1つのシンボル、2つの文字列、および2つのハッシュオブジェクト)のみが作成されます。


ただし、リンクの例では、異なる値を含むシンボルが示されていますが、シンボルの名前とメモリ位置は同じです。それらが出力されるとき、それらは異なる値を持っています、それは私が得られない部分です。確かにそれらは同じ値を含むべきですか?
ケザー2010

1
まだ混乱していることを説明するために編集を行いました。私の脳は計算できません;)
Kezzer 2010

48
シンボルに含まれていません。値です。ハッシュには値が含まれています。
ムラデンJablanović

5
それはですHash(あなたのコード内で{... => ...}で作成された)が格納され、キー/値のペアこと、ではないSymbol自分自身です。SymbolS(例えば:symbolまたは:symまたは:ruby)は、ペアのキーです。Hash実行の一部としてのみ、それらは何かを「指し示し」ます。
James A. Rosen

1
シンボルは値ではなくハッシュのキーとして使用されているため、異なる可能性があります。これは、key1 = 'ruby'およびhash1 = {key1 => 'value' ...} hash2 = {と言うのと似ています。 key1 => 'value2' ...}。
Joshua Olson

53

こんな風に思ったら、シンボルをかじることができました。Ruby文字列は、多数のメソッドとプロパティを持つオブジェクトです。人々はキーに文字列を使用するのが好きで、文字列がキーに使用される場合、それらの余分なメソッドはすべて使用されません。そこで彼らはシンボルを作成しました。シンボルは、適切なキーであるために必要なものを除いて、すべての機能が削除された文字列オブジェクトです。

記号は定数文字列と考えてください。


2
投稿を読んで、これはおそらく私にとって最も理にかなっています。:rubyはメモリのどこかに保存されているだけです。どこかで「ruby」を使用し、次に「ruby」をもう一度どこかで使用すると、複製になります。したがって、シンボルを使用することは、共通データの重複を減らす方法です。あなたが言うように、定数文字列。そのシンボルを再び使用するための基礎となるメカニズムがあると思いますか?
ケザー2010年

@Kezzerこの答えは本当に良いですし、私には正しいようですが、あなたのコメントは何か違うことを言っていて、間違っているか誤解を招きます、あなたのコメントは文字列とデータの重複について話します、そしてそれが記号の理由です、それは間違っているか誤解を招きます。シンボルが複数回メモリスペースを消費することはありませんが、多くの言語の文字列に対してもそれを使用できます。たとえば、「abc」や他の場所で「abc」と記述した場合、コンパイラは同じ値の文字列を認識して格納します。同じ場所で同じオブジェクトにします。これは文字列インターンと呼ばれ、c#がそれを行います。
barlop 2018年

それで、それは基本的に文字列の信じられないほど軽量なバージョンですか?
stevec

34

記号にまたは:rubyが含まれていません。シンボルは単なるシンボルです。それはあなたのハッシュであり、それぞれが同じキーによって指し示されるそれらの値を含んでいます。"red""programming":ruby:rubypatient1patient2

このように考えてみてください。クリスマスの朝に居間に入ると、「Kezzer」と書かれたタグが付いた2つのボックスが表示されます。オンには靴下があり、もう一方には石炭があります。同じ名前であっても、混乱して「Kezzer」に靴下と石炭の両方を含める方法を尋ねる必要はありません。名前に(くだらない)プレゼントが含まれていないからです。それは彼らを指しているだけです。同様に、:rubyハッシュに値が含まれていません。値を指しているだけです。


2
この答えは完全に理にかなっています。
Vass 2014年

これは、ハッシュとシンボルが完全に混ざり合っているように聞こえます。シンボルは値を指していません。ハッシュ内にあるときにそうしていると言いたいのであれば、それは議論の余地があるかもしれませんが、シンボルはハッシュ内にある必要はありません。mystring = :steveT シンボルは何も指していないと言えます。ハッシュ内のキーには値が関連付けられており、キーはシンボルである可能性があります。ただし、シンボルはハッシュに含まれている必要はありません。
barlop 2018年

27

あなたが行った宣言は、シンボルの値をそれが何であるか以外のものとして定義していると推測しているかもしれません。実際、Symbolは、一定のままである単なる「内部化された」文字列値です。単純な整数識別子を使用して格納されているため、多数の可変長文字列を管理するよりも効率的であるため、頻繁に使用されます。

あなたの例を考えてみましょう:

patient1 = { :ruby => "red" }

これは次のように読む必要があります。「変数patient1を宣言し、それをハッシュとして定義します。このストアでは、キー(記号「ruby」)の下に値「red」を格納します。」

これを書く別の方法は次のとおりです。

patient1 = Hash.new
patient1[:ruby] = 'red'

puts patient1[:ruby]
# 'red'

割り当てを行っているときに、返される結果が最初に割り当てたものと同じであることは驚くことではありません。

シンボルの概念は、他のほとんどの言語の機能ではないため、少し混乱する可能性があります。

値が同一であっても、各Stringオブジェクトは異なります。

[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
  puts v.inspect + ' ' + v.object_id.to_s
end

# "foo" 2148099960
# "foo" 2148099940
# "foo" 2148099920
# "bar" 2148099900
# "bar" 2148099880
# "bar" 2148099860

同じ値を持つすべてのシンボルは、同じオブジェクトを参照します。

[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
  puts v.inspect + ' ' + v.object_id.to_s
end

# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668

文字列をシンボルに変換すると、同じ値が同じ一意のシンボルにマップされます。

[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
  v = v.to_sym
  puts v.inspect + ' ' + v.object_id.to_s
end

# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668

同様に、SymbolからStringに変換すると、毎回異なる文字列が作成されます。

[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
  v = v.to_s
  puts v.inspect + ' ' + v.object_id.to_s
end

# "foo" 2148097820
# "foo" 2148097700
# "foo" 2148097580
# "bar" 2148097460
# "bar" 2148097340
# "bar" 2148097220

シンボル値は内部ハッシュテーブルから取得されたものと考えることができ、単純なメソッド呼び出しを使用してシンボルにエンコードされたすべての値を確認できます。

Symbol.all_values

# => [:RUBY_PATCHLEVEL, :vi_editing_mode, :Separator, :TkLSHFT, :one?, :setuid?, :auto_indent_mode, :setregid, :back, :Fail, :RET, :member?, :TkOp, :AP_NAME, :readbyte, :suspend_context, :oct, :store, :WNOHANG, :@seek, :autoload, :rest, :IN_INPUT, :close_read, :type, :filename_quote_characters=, ...

コロン表記または.to_symを使用して新しいシンボルを定義すると、このテーブルは大きくなります。


17

シンボルはポインタではありません。値は含まれていません。記号は単にです:rubyはシンボルで:rubyあり、それがすべてです。値は含まれず、何も実行せず、シンボルとして存在するだけ:rubyです。記号:rubyは、数値1と同じ値です。数値1以外の別の値を指すことはありません。


13
patient1.each_key {|key| puts key.to_s}

では、何が出力されますか?「赤」、それとも「プログラミング」?

どちらも「ruby」を出力しません。

シンボルとハッシュを混同しています。それらは関連していませんが、一緒に役立ちます。問題の記号は:ruby;です。ハッシュ内の値とは関係がなく、内部整数表現は常に同じであり、その「値」(文字列に変換された場合)は常に「ruby」になります。


10

要するに

シンボルは、人間が読める不変の表現を作成するという問題を解決します。また、文字列よりもランタイムでのルックアップが簡単であるという利点もあります。再利用できる名前やラベルのように考えてください。

なぜ:redが "red"よりも優れているのか

動的オブジェクト指向言語では、読み取り可能な参照を使用して、複雑でネストされたデータ構造を作成します。ハッシュは、一般的な使用例で、少なくとも、各インスタンスに、ユニークな-あなたはユニークなキーに値をマップします。ハッシュごとに複数の「赤い」キーを持つことはできません。

ただし、文字列キーの代わりに数値インデックスを使用すると、プロセッサの効率が向上します。そのため、速度と読みやすさの間の妥協点として記号が導入されました。記号は、同等の文字列よりもはるかに簡単に解決されます。人間が読める形式であり、ランタイムがシンボルを解決しやすいことにより、動的言語への理想的な追加となります。

利点

シンボルは不変であるため、ランタイム全体で共有できます。2つのハッシュインスタンスに赤い項目に対する共通の辞書式または意味上の必要性がある場合、記号:redは、文字列「red」が2つのハッシュに必要とするメモリの約半分を使用します。

:redは常にメモリ内の同じ場所に解決されるため、メモリをほとんど増やすことなく100個のハッシュインスタンスで再利用できますが、「red」を使用すると、各ハッシュインスタンスに可変文字列を格納する必要があるため、メモリコストが増加します。作成。

Rubyが実際にシンボル/文字列をどのように実装するかはわかりませんが、シンボルは固定表現であるため、ランタイムでの実装オーバーヘッドが少ないことは明らかです。プラス記号は、引用符で囲まれた文字列よりも入力に必要な文字が1つ少なく、入力が少ないことが真のルビー主義者の永遠の追求です。

概要

:redのような記号を使用すると、文字列比較操作のコストと各文字列インスタンスをメモリに格納する必要があるため、オーバーヘッドが少なくて文字列表現が読みやすくなります。


4

ハッシュテーブルに関するウィキペディアの記事を読むことをお勧めします。これは、{:ruby => "red"}実際の意味を理解するのに役立つと思います。

状況の理解に役立つ可能性のある別の演習:検討してください{1 => "red"}。意味的には、これは「の値を1に設定する」という意味ではありません"red"。これはRubyでは不可能です。むしろ、「ハッシュオブジェクトを作成"red"し、キーの値を格納する」という意味1です。


3
patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1patient2両方ともハッシュです、それは問題ありません。:rubyただし、はシンボルです。以下を出力する場合:

patient1.each_key {|key| puts key.to_s}

では、何が出力されますか?「赤」、それとも「プログラミング」?

もちろん、どちらでもありません。出力はになりますruby。ところで、代わりにIRBに入力するだけで、質問を入力するよりも短い時間で見つけることができたはずです。

なぜだろう、それは可能redprogramming?シンボルは常にそれ自体に評価されます。シンボルの値は:rubyシンボル:ruby自体であり、シンボルの文字列表現は:ruby文字列値"ruby"です。

[BTW:putsとにかく、常に引数を文字列に変換します。呼び出す必要はありませんto_s。]


現在のマシンにIRBがないので、インストールすることもできません。そのため、お詫びします。
ケザー2010

2
@Kezzer:心配ありません、私はただ興味がありました。問題に深く埋もれてしまい、最も単純なものすら見えなくなることがあります。基本的にあなたの質問をIRBに切り取って貼り付けたとき、「なぜ彼は自分でそれをしなかったのか」と疑問に思いました。そして、心配しないでください。答えが「ただ実行するだけです!」であるときに「この印刷物は何ですか」と尋ねるのはあなたが最初ではありません(最後になることもありません)。ところで、これがインスタントIRBで、いつでも、どこでも、インストールは必要ありません。TryRuby.OrgまたはRuby-Versions.Netを使用すると、これまでにリリースされたMRIのすべてのバージョン+ YARV + JRuby + Rubinius + REEにSSHでアクセスできます。
イェルクWミッターク

ありがとう、今それで遊んでください。まだ少し混乱しているので、もう一度やり直します。
ケザー2010

0

私はRubyを初めて使用しますが、これは簡単な見方だと思います(願っていますか?)...

シンボルは変数でも定数でもありません。値を表すものでも、値を指すものでもありません。シンボルは値です。

それは、オブジェクトのオーバーヘッドのない文字列だけです。テキストとテキストのみ。

したがって、この:

"hellobuddy"

これと同じです:

:hellobuddy

たとえば、:hellobuddy.upcaseを実行できない場合を除きます。これは文字列値であり、文字列値のみです。

同様に、これ:

greeting =>"hellobuddy"

これと同じです:

greeting => :hellobuddy

しかし、繰り返しになりますが、文字列オブジェクトのオーバーヘッドはありません。


-1

これに頭を包む簡単な方法の1つは、「記号ではなく文字列を使用した場合はどうなるか」と考えることです。

patient1 = { "ruby" => "red" }
patient2 = { "ruby" => "programming" }

まったく混乱しませんよね?ハッシュのキーとして「ruby」を使用しています

"ruby"は文字列リテラルなので、それが値です。メモリアドレスまたはポインタは使用できません。を呼び出すたび"ruby"に、そのインスタンスを作成します。つまり、同じ値を含む新しいメモリセルを作成します- "ruby"

次に、ハッシュは「私のキー値は何ですか?ああ、そうです"ruby"。次に、その値を「赤」または「プログラミング」にマップします。つまり、またはを:ruby逆参照しません。ハッシュまたはにマップします。"red""programming" :ruby"red""programming"

記号を使用する場合と比較してください

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

の値:ruby"ruby"、効果的にです。

どうして?シンボルは本質的に文字列定数であるためです。定数には複数のインスタンスはありません。同じメモリアドレスです。また、メモリアドレスは、逆参照されると特定の値を持ちます。シンボルの場合、ポインタ名はシンボルであり、逆参照された値は文字列であり、この場合はシンボル名と一致し"ruby"ます。

ハッシュ内では、シンボル、ポインターではなく、差分値を使用しています。を使用していません:rubyが、"ruby"。次に、ハッシュはキーを検索します。ハッシュの定義方法に応じて"ruby"、値は"red"また"programming"はです。

パラダイムシフトと持ち帰りの概念は、シンボルの値が、ハッシュのキーが与えられた場合に、ハッシュによってマップされた値とは完全に別個の概念であるというものです。


この説明の誤謬や誤りは何ですか、反対派?学習のために好奇心が強い。
ahnbizcad 2017年

類推が一部の人にとって不快かもしれないからといって、それが欠陥があるという意味ではありません。
ahnbizcad 2017年

2
必ずしもエラーは見られませんが、この回答であなたが言おうとしていることを特定することは非常に困難です。
リバースエンジニアリング

xは、通訳を行うコンテキスト/エンティティに基づいて、異なる方法で相互作用および概念化されます。ものすごく単純。
ahnbizcad 2018年

答えを見直しました。フィードバックをお寄せいただきありがとうございます。
ahnbizcad
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.