Wie man Symbole in Ruby versteht


85

Obwohl ich " Ruby-Symbole verstehen " gelesen habe , bin ich immer noch verwirrt über die Darstellung der Daten im Speicher, wenn es um die Verwendung von Symbolen geht. Wenn ein Symbol, von denen zwei in verschiedenen Objekten enthalten sind, am selben Speicherort vorhanden ist, wie kommt es dann, dass sie unterschiedliche Werte enthalten ? Ich hätte erwartet, dass derselbe Speicherort den gleichen Wert enthält.

Dies ist ein Zitat aus dem Link:

Im Gegensatz zu Zeichenfolgen werden gleichnamige Symbole initialisiert und existieren während einer Ruby-Sitzung nur einmal im Speicher

Ich verstehe nicht, wie es gelingt, die im selben Speicherort enthaltenen Werte zu unterscheiden.

Betrachten Sie dieses Beispiel:

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

patient1und patient2sind beide Hashes, das ist in Ordnung. :rubyist jedoch ein Symbol. Wenn wir Folgendes ausgeben würden:

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

Was wird dann ausgegeben? "red"oder "programming"?

Ich vergesse Hashes für eine Sekunde und denke, ein Symbol ist ein Zeiger auf einen Wert. Die Fragen, die ich habe, sind:

  • Kann ich einem Symbol einen Wert zuweisen?
  • Ist ein Symbol nur ein Zeiger auf eine Variable mit einem Wert darin?
  • Wenn Symbole global sind, bedeutet das, dass ein Symbol immer auf eine Sache verweist?

1
Es wird ": ruby" ausgegeben, da Sie ein Symbol drucken. Wenn Sie sagen puts patient1[:ruby], wird "rot" gedruckt, wenn Sie sagen puts patient2[:ruby], wird "Programmierung" gedruckt.
Ross

1
Ein Symbol ist KEIN Zeiger auf einen Wert. Intern ist ein Symbol nur eine ganze Zahl.
Akuhn

Antworten:


62

Bedenken Sie:

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

Wenn Sie also ein Symbolobjekt erstellen, verweist es, solange sein Inhalt identisch ist, auf dasselbe Objekt im Speicher. Dies ist kein Problem, da ein Symbol ein unveränderliches Objekt ist . Saiten sind veränderlich.


(Als Antwort auf den Kommentar unten)

Im Originalartikel wird der Wert nicht in einem Symbol gespeichert, sondern in einem Hash. Bedenken Sie:

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

Dadurch werden sechs Objekte im Speicher erstellt - vier Zeichenfolgenobjekte und zwei Hashobjekte.

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

Dadurch werden nur fünf Objekte im Speicher erstellt - ein Symbol, zwei Zeichenfolgen und zwei Hash-Objekte.


Das Beispiel im Link zeigt jedoch die Symbole, die unterschiedliche Werte enthalten, aber das Symbol hat denselben Namen und denselben Speicherort. Wenn sie ausgegeben werden, haben sie unterschiedliche Werte, das ist der Teil, den ich nicht bekomme. Sicherlich sollten sie den gleichen Wert enthalten?
Kezzer

1
Ich habe gerade eine Bearbeitung vorgenommen, um zu erklären, wie ich immer noch verwirrt bin. Mein Gehirn kann nicht rechnen;)
Kezzer

48
Symbole enthalten keine Werte, sie sind Werte. Hashes enthalten Werte.
Mladen Jablanović

5
Es ist das Hash(erstellt von {... => ...} in Ihrem Code), das Schlüssel / Wert-Paare speichert, nicht das Symbols selbst. Die Symbols (zB :symboloder oder :symoder :ruby) sind die Schlüssel in den Paaren. Nur als Teil von Hash"zeigen" sie auf irgendetwas.
James A. Rosen

1
Das Symbol wird als Schlüssel im Hash verwendet, nicht als Wert. Deshalb können sie unterschiedlich sein. Es ähnelt der Verwendung von key1 = 'ruby' und hash1 = {key1 => 'value' ...} hash2 = { key1 => 'value2' ...}.
Joshua Olson

52

Ich konnte Symbole erkennen, wenn ich so darüber nachdachte. Eine Ruby-Zeichenfolge ist ein Objekt mit einer Reihe von Methoden und Eigenschaften. Leute verwenden gerne Zeichenfolgen für Schlüssel, und wenn die Zeichenfolge für einen Schlüssel verwendet wird, werden all diese zusätzlichen Methoden nicht verwendet. Also machten sie Symbole, bei denen es sich um Zeichenfolgenobjekte handelt, bei denen alle Funktionen entfernt wurden, mit Ausnahme derjenigen, die für einen guten Schlüssel erforderlich sind.

Stellen Sie sich Symbole als konstante Zeichenfolgen vor.


2
Wenn ich die Beiträge durchlese, ist dieser für mich wahrscheinlich am sinnvollsten. : ruby ​​wird nur irgendwo im Speicher gespeichert. Wenn ich irgendwo "ruby" verwende, dann irgendwo wieder "ruby", ist es nur eine Vervielfältigung. Die Verwendung von Symbolen ist daher eine Möglichkeit, die Duplizierung allgemeiner Daten zu reduzieren. Wie Sie sagen, konstante Zeichenfolgen. Ich denke, es gibt einen zugrunde liegenden Mechanismus, der dieses Symbol wieder findet, um es zu verwenden?
Kezzer

@Kezzer Diese Antwort ist wirklich gut und scheint mir richtig zu sein, aber Ihr Kommentar sagt etwas anderes und ist falsch oder irreführend. Ihr Kommentar spricht von der Verdoppelung von Daten mit Zeichenfolgen und dass dies der Grund für Symbole ist, das ist falsch oder irreführend Das Symbol verbraucht mehrmals nicht mehr Speicherplatz, aber Sie können dies auch für Zeichenfolgen in vielen Sprachen verwenden, z. B. in einigen Programmiersprachen, wenn Sie "abc" und an anderer Stelle "abc" schreiben. Der Compiler sieht, dass es sich um dieselbe Zeichenfolge handelt, und speichert sie an derselben Stelle, die es zum selben Objekt macht, das als String-Internierung bezeichnet wird, und c # macht das.
Barlop

34

Das Symbol :rubyenthält kein "red"oder "programming". Das Symbol :rubyist nur das Symbol :ruby. Es sind Ihre Hashes, patient1und patient2jeder enthält diese Werte, auf die jeweils mit demselben Schlüssel verwiesen wird.

Stellen Sie sich das so vor: Wenn Sie am Weihnachtsmorgen ins Wohnzimmer gehen und zwei Kisten mit einem Etikett sehen, auf denen "Kezzer" steht. On hat Socken drin und der andere hat Kohle. Sie werden nicht verwirrt sein und fragen, wie "Kezzer" sowohl Socken als auch Kohle enthalten kann, obwohl es der gleiche Name ist. Weil der Name die (beschissenen) Geschenke nicht enthält. Es zeigt nur auf sie. Enthält in ähnlicher Weise :rubynicht die Werte in Ihrem Hash, sondern zeigt nur auf sie.


2
Diese Antwort ist absolut sinnvoll.
Vass

Das klingt nach einer totalen Verwechslung von Hashes und Symbolen. Ein Symbol zeigt nicht auf einen Wert. Wenn Sie sagen möchten, dass dies in einem Hash der Fall ist, ist dies möglicherweise fraglich, aber ein Symbol muss nicht in einem Hash enthalten sein. Man kann sagen, dass mystring = :steveT das Symbol auf nichts zeigt. Einem Schlüssel in einem Hash ist ein Wert zugeordnet, und der Schlüssel kann ein Symbol sein. Aber ein Symbol muss nicht in einem Hash stehen.
Barlop

27

Sie können davon ausgehen, dass die von Ihnen abgegebene Erklärung den Wert eines Symbols als etwas anderes definiert als das, was es ist. Tatsächlich ist ein Symbol nur ein "internalisierter" String-Wert, der konstant bleibt. Da sie unter Verwendung einer einfachen Ganzzahlkennung gespeichert werden, werden sie häufig verwendet, da dies effizienter ist als die Verwaltung einer großen Anzahl von Zeichenfolgen variabler Länge.

Nehmen Sie den Fall Ihres Beispiels:

patient1 = { :ruby => "red" }

Dies sollte wie folgt gelesen werden: "Deklarieren Sie eine Variable patient1 und definieren Sie sie als Hash. Speichern Sie in diesem den Wert 'rot' unter dem Schlüssel (Symbol 'ruby')."

Eine andere Art, dies zu schreiben, ist:

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

puts patient1[:ruby]
# 'red'

Während Sie eine Aufgabe ausführen, ist es nicht verwunderlich, dass das Ergebnis, das Sie zurückerhalten, mit dem identisch ist, mit dem Sie es ursprünglich zugewiesen haben.

Das Symbolkonzept kann etwas verwirrend sein, da es in den meisten anderen Sprachen nicht enthalten ist.

Jedes String-Objekt ist unterschiedlich, auch wenn die Werte identisch sind:

[ "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

Jedes Symbol mit demselben Wert bezieht sich auf dasselbe Objekt:

[ :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

Durch das Konvertieren von Zeichenfolgen in Symbole werden identische Werte demselben eindeutigen Symbol zugeordnet:

[ "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

Ebenso wird beim Konvertieren von Symbol zu Zeichenfolge jedes Mal eine eindeutige Zeichenfolge erstellt:

[ :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

Sie können sich Symbolwerte als aus einer internen Hash-Tabelle gezogen vorstellen und alle Werte anzeigen, die mit einem einfachen Methodenaufruf in Symbole codiert wurden:

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=, ...

Wenn Sie neue Symbole entweder durch die Doppelpunktnotation oder mit .to_sym definieren, wächst diese Tabelle.


17

Symbole sind keine Zeiger. Sie enthalten keine Werte. Symbole einfach sind . :rubyist das Symbol :rubyund das ist alles was dazu gehört. Es gibt keinen Wert enthalten, es nicht tun alles, es existiert nur als Symbol :ruby. Das Symbol :rubyist genau wie die Zahl 1 ein Wert. Es zeigt nicht mehr auf einen anderen Wert als die Zahl 1.


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

Was wird dann ausgegeben? "rot" oder "programmieren"?

Es wird auch nicht "Ruby" ausgegeben.

Sie verwirren Symbole und Hashes. Sie sind nicht verwandt, aber sie sind zusammen nützlich. Das fragliche Symbol ist :ruby; Es hat nichts mit den Werten im Hash zu tun, und die interne Ganzzahldarstellung ist immer dieselbe, und der "Wert" (wenn er in eine Zeichenfolge konvertiert wird) ist immer "rubin".


10

Zusamenfassend

Symbole lösen das Problem der Erstellung von lesbaren, unveränderlichen Darstellungen, die für die Laufzeit einfacher zu suchen sind als Zeichenfolgen. Stellen Sie sich das wie einen Namen oder ein Etikett vor, das wiederverwendet werden kann.

Warum: Rot ist besser als "Rot"

In dynamischen objektorientierten Sprachen erstellen Sie komplexe, verschachtelte Datenstrukturen mit lesbaren Referenzen. Der Hash ist ein häufiger Anwendungsfall, bei dem Sie Werte eindeutigen Schlüsseln zuordnen - zumindest für jede Instanz eindeutig. Sie können nicht mehr als einen "roten" Schlüssel pro Hash haben.

Es wäre jedoch prozessoreffizienter, einen numerischen Index anstelle von Zeichenfolgenschlüsseln zu verwenden. So wurden Symbole als Kompromiss zwischen Geschwindigkeit und Lesbarkeit eingeführt. Symbole werden viel einfacher aufgelöst als die entsprechende Zeichenfolge. Durch die menschliche Lesbarkeit und die einfache Auflösung von Symbolen zur Laufzeit sind Symbole eine ideale Ergänzung zu einer dynamischen Sprache.

Leistungen

Da Symbole unveränderlich sind, können sie zur Laufzeit gemeinsam genutzt werden. Wenn zwei Hash-Instanzen einen gemeinsamen lexikografischen oder semantischen Bedarf für ein rotes Element haben, würde das Symbol: Rot ungefähr die Hälfte des Speichers verbrauchen, den die Zeichenfolge "rot" für zwei Hashes benötigt hätte.

Da: rot immer wieder an dieselbe Stelle im Speicher aufgelöst wird, kann es über hundert Hash-Instanzen hinweg fast ohne Speichererhöhung wiederverwendet werden, während die Verwendung von "rot" Speicherkosten verursacht, da jede Hash-Instanz die veränderbare Zeichenfolge speichern müsste Schaffung.

Nicht sicher, wie Ruby tatsächlich Symbole / Zeichenfolgen implementiert, aber ein Symbol bietet eindeutig weniger Implementierungsaufwand zur Laufzeit, da es sich um eine feste Darstellung handelt. Plus-Symbole benötigen ein Zeichen weniger als eine Zeichenfolge in Anführungszeichen, und weniger Eingabe ist das ewige Streben nach echten Rubyisten.

Zusammenfassung

Mit einem Symbol wie: rot erhalten Sie die Lesbarkeit der Zeichenfolgendarstellung mit weniger Aufwand aufgrund der Kosten für Zeichenfolgenvergleichsoperationen und der Notwendigkeit, jede Zeichenfolgeninstanz im Speicher zu speichern.


4

Ich würde empfehlen, den Wikipedia-Artikel über Hash-Tabellen zu lesen - ich denke, es wird Ihnen helfen, ein Gefühl dafür zu bekommen, was {:ruby => "red"}wirklich bedeutet.

Eine weitere Übung, die Ihnen helfen könnte, die Situation zu verstehen: Überlegen Sie {1 => "red"}. Semantisch bedeutet dies nicht "setze den Wert von 1auf "red"", was in Ruby unmöglich ist. Es bedeutet vielmehr "ein Hash- Objekt erstellen und den Wert "red"für den Schlüssel speichern" 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

patient1und patient2sind beide Hashes, das ist in Ordnung. :rubyist jedoch ein Symbol. Wenn wir Folgendes ausgeben würden:

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

Was wird dann ausgegeben? "rot" oder "programmieren"?

Natürlich auch nicht. Die Ausgabe wird sein ruby. Übrigens, Sie hätten es in kürzerer Zeit herausfinden können, als Sie für die Eingabe der Frage benötigt haben, indem Sie sie stattdessen einfach in IRB eingegeben haben.

Warum sollte es sein redoder programming? Symbole bewerten sich immer selbst. Der Wert des Symbols :rubyist das Symbol :rubyselbst und die Zeichenfolgendarstellung des Symbols :rubyist der Zeichenfolgenwert "ruby".

[Übrigens: putskonvertiert seine Argumente sowieso immer in Strings. Es besteht keine Notwendigkeit, to_sdarauf zurückzugreifen.]


Ich habe kein IRB auf dem aktuellen Computer und kann es auch nicht installieren. Deshalb entschuldige ich mich dafür.
Kezzer

2
@ Kezerzer: Keine Sorge, ich war nur neugierig. Manchmal vergraben Sie sich so tief in ein Problem, dass Sie nicht einmal mehr die einfachsten Dinge sehen können. Als ich Ihre Frage im Grunde genommen in IRB ausgeschnitten und eingefügt habe, habe ich mich nur gefragt: "Warum hat er das nicht selbst gemacht?" Und keine Sorge, Sie sind nicht der Erste (und Sie werden auch nicht der Letzte sein), der fragt "Was macht dieser Druck?", Wenn die Antwort "Einfach ausführen!" Lautet. Übrigens: Hier ist Ihr sofortiger IRB, überall und jederzeit, keine Installation erforderlich: TryRuby.Org oder Ruby-Versions.Net bietet Ihnen SSH-Zugriff auf alle jemals veröffentlichten MRT-Versionen + YARV + JRuby + Rubinius + REE.
Jörg W Mittag

Danke, jetzt einfach rumspielen. Ich bin immer noch ein wenig verwirrt, also gehe ich noch einmal darüber nach.
Kezzer

0

Ich bin neu bei Ruby, aber ich denke (hoffe?), Dass dies eine einfache Möglichkeit ist, es zu betrachten ...

Ein Symbol ist keine Variable oder Konstante. Es steht nicht für einen Wert oder zeigt auf ihn. Ein Symbol ist ein Wert.

Alles, was es ist, ist eine Zeichenfolge ohne den Objekt-Overhead. Der Text und nur der Text.

Also das:

"hellobuddy"

Ist das gleiche wie das:

:hellobuddy

Außer Sie können zum Beispiel nicht tun: hellobuddy.upcase. Es ist der String-Wert und NUR der String-Wert.

Ebenso dies:

greeting =>"hellobuddy"

Ist das gleiche wie das:

greeting => :hellobuddy

Aber auch ohne den Overhead des String-Objekts.


-1

Eine einfache Möglichkeit, Ihren Kopf darum zu wickeln, besteht darin, zu denken: "Was wäre, wenn ich eine Zeichenfolge anstelle eines Symbols verwenden würde?

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

Es ist überhaupt nicht verwirrend, oder? Sie verwenden "Ruby" als Schlüssel in einem Hash .

"ruby"ist ein String-Literal, das ist also der Wert. Die Speicheradresse oder der Zeiger steht Ihnen nicht zur Verfügung. Jedes Mal "ruby", wenn Sie aufrufen , erstellen Sie eine neue Instanz davon, dh eine neue Speicherzelle mit demselben Wert - "ruby".

Der Hash geht dann : „Was ist mein Schlüsselwert? Oh , es ist "ruby". Dann ordnet diesen Wert auf‚rot‘oder‚Programmierung‘. Mit anderen Worten, :rubynicht dereferenzieren nicht "red"oder "programming". Die Hash - Karten :ruby auf "red"oder "programming".

Vergleichen Sie das damit, wenn wir Symbole verwenden

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

Der Wert von :rubyist auch "ruby"effektiv.

Warum? Weil Symbole im Wesentlichen Zeichenfolgenkonstanten sind . Konstanten haben nicht mehrere Instanzen. Es ist die gleiche Speicheradresse. Und eine Speicheradresse hat einen bestimmten Wert, sobald sie dereferenziert ist. Bei Symbolen ist der Zeigername das Symbol, und der dereferenzierte Wert ist eine Zeichenfolge, die in diesem Fall mit dem Symbolnamen übereinstimmt "ruby".

In einem Hash verwenden Sie nicht das Symbol, den Zeiger, sondern den verzögerten Wert. Du benutzt nicht :ruby, aber "ruby". Der Hash sucht dann nach Schlüssel "ruby", der Wert ist "red"oder "programming", je nachdem, wie Sie den Hash definiert haben.

Das Paradigmenwechsel- und Take-Home-Konzept besteht darin, dass der Wert eines Symbols ein völlig anderes Konzept ist als ein Wert, der durch einen Hash zugeordnet wird, wenn ein Schlüssel für diesen Hash angegeben wird.


Was ist der Irrtum oder Fehler in dieser Erklärung, Downvoter? neugierig um des Lernens willen.
Ahnbizcad

Nur weil eine Analogie für manche unangenehm sein kann, heißt das nicht, dass sie fehlerhaft ist.
Ahnbizcad

2
Ich sehe nicht unbedingt Fehler, aber es ist äußerst schwierig festzustellen, was Sie in dieser Antwort sagen wollen.
Reverse Engineered

x wird je nach Kontext / Entität, die die Interpretation durchführt, unterschiedlich interpretiert und konzeptualisiert. ziemlich einfach.
Ahnbizcad

überarbeitete die Antwort. Danke für die Rückmeldung.
Ahnbizcad
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.