Warum in Ruby Symbole als Hash-Schlüssel verwenden?

161

Oft verwenden Leute Symbole als Schlüssel in einem Ruby-Hash.

Was ist der Vorteil gegenüber der Verwendung einer Zeichenfolge?

Z.B:

hash[:name]

vs.

hash['name']
Max
quelle

Antworten:

226

TL; DR:

Die Verwendung von Symbolen spart nicht nur Zeit bei Vergleichen, sondern auch Speicher, da sie nur einmal gespeichert werden.

Ruby-Symbole sind unveränderlich (können nicht geändert werden), was das Nachschlagen erheblich erleichtert

Kurze (ish) Antwort:

Die Verwendung von Symbolen spart nicht nur Zeit bei Vergleichen, sondern auch Speicher, da sie nur einmal gespeichert werden.

Symbole in Ruby sind im Grunde genommen "unveränderliche Zeichenfolgen" . Dies bedeutet, dass sie nicht geändert werden können. Dies bedeutet, dass dasselbe Symbol, wenn es im gesamten Quellcode mehrmals referenziert wird, immer als dieselbe Entität gespeichert wird, z. B. dieselbe Objekt-ID hat .

Saiten hingegen sind veränderlich und können jederzeit geändert werden. Dies bedeutet, dass Ruby jede Zeichenfolge, die Sie im gesamten Quellcode erwähnen, in einer separaten Entität speichern muss. Wenn Sie beispielsweise einen Zeichenfolgennamen mehrfach in Ihrem Quellcode erwähnt haben, muss Ruby diese alle in separaten Zeichenfolgenobjekten speichern, da diese könnte sich später ändern (das ist die Natur eines Ruby-Strings).

Wenn Sie eine Zeichenfolge als Hash-Schlüssel verwenden, muss Ruby die Zeichenfolge auswerten und deren Inhalt überprüfen (und eine Hash-Funktion darauf berechnen) und das Ergebnis mit den (Hash-) Werten der Schlüssel vergleichen, die bereits im Hash gespeichert sind .

Wenn Sie ein Symbol als Hash-Schlüssel verwenden, bedeutet dies implizit, dass es unveränderlich ist, sodass Ruby im Grunde nur einen Vergleich der (Hash-Funktion der) Objekt-ID mit den (Hash-) Objekt-IDs der Schlüssel durchführen kann, die bereits in gespeichert sind der Hash. (viel schneller)

Nachteil: Jedes Symbol belegt einen Platz in der Symboltabelle des Ruby-Interpreters, der niemals freigegeben wird. Symbole werden niemals durch Müll gesammelt. Ein Eckfall ist also, wenn Sie eine große Anzahl von Symbolen haben (z. B. automatisch generierte). In diesem Fall sollten Sie bewerten, wie sich dies auf die Größe Ihres Ruby-Interpreters auswirkt.

Anmerkungen:

Wenn Sie Zeichenfolgenvergleiche durchführen, kann Ruby Symbole nur anhand ihrer Objekt-IDs vergleichen, ohne sie auswerten zu müssen. Das ist viel schneller als der Vergleich von Zeichenfolgen, die ausgewertet werden müssen.

Wenn Sie auf einen Hash zugreifen, wendet Ruby immer eine Hash-Funktion an, um einen "Hash-Schlüssel" aus dem von Ihnen verwendeten Schlüssel zu berechnen. Sie können sich so etwas wie einen MD5-Hash vorstellen. Und dann vergleicht Ruby diese "Hash-Schlüssel" miteinander.

Lange Antwort:

https://web.archive.org/web/20180709094450/http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings

http://www.randomhacks.net.s3-website-us-east-1.amazonaws.com/2007/01/20/13-ways-of-looking-at-a-ruby-symbol/

Tilo
quelle
5
Zu Ihrer Information, Symbole werden in der nächsten Version von Ruby GCd sein
Ajedi32
2
Außerdem werden Strings automatisch eingefroren, wenn sie in Ruby als Hash-Schlüssel verwendet werden. Es ist also nicht genau richtig, dass Strings veränderlich sind, wenn man in diesem Zusammenhang darüber spricht.
Ajedi32
1
Großartige Einblicke in das Thema und den ersten Link im Abschnitt "Lange Antwort" werden entfernt oder migriert.
Hbksagar
2
Symbole sind Müll, der in Ruby 2.2
Marc-André Lafortune am
2
Gute Antwort! Auf der Trolling-Seite ist Ihre "kurze Antwort" auch lang genug. ;)
Technophyle
22

Der Grund ist die Effizienz mit mehreren Gewinnen gegenüber einem String:

  1. Symbole sind unveränderlich, daher die Frage "Was passiert, wenn sich der Schlüssel ändert?" muss nicht gefragt werden.
  2. Zeichenfolgen werden in Ihrem Code dupliziert und benötigen normalerweise mehr Speicherplatz.
  3. Hash-Lookups müssen den Hash der Schlüssel berechnen, um sie zu vergleichen. Dies ist O(n)für Strings und konstant für Symbole.

Darüber hinaus hat Ruby 1.9 eine vereinfachte Syntax nur für Hash mit Symbolschlüsseln (z. B. h.merge(foo: 42, bar: 6)) eingeführt, und Ruby 2.0 verfügt über Schlüsselwortargumente , die nur für Symbolschlüssel funktionieren.

Anmerkungen :

1) Es könnte Sie überraschen, dass Ruby StringSchlüssel anders behandelt als jeder andere Typ. Tatsächlich:

s = "foo"
h = {}
h[s] = "bar"
s.upcase!
h.rehash   # must be called whenever a key changes!
h[s]   # => nil, not "bar"
h.keys
h.keys.first.upcase!  # => TypeError: can't modify frozen string

Nur für Zeichenfolgenschlüssel verwendet Ruby anstelle des Objekts selbst eine eingefrorene Kopie.

2) Die Buchstaben "b", "a" und "r" werden nur einmal für alle Vorkommen :barin einem Programm gespeichert . Vor Ruby 2.2 war es eine schlechte Idee, ständig neue zu erstellen Symbols, die nie wiederverwendet wurden, da sie für immer in der globalen Symbol-Nachschlagetabelle verbleiben würden. Ruby 2.2 wird sie mit Müll sammeln, also keine Sorge.

3) Tatsächlich hat das Berechnen des Hashs für ein Symbol in Ruby 1.8.x keine Zeit in Anspruch genommen, da die Objekt-ID direkt verwendet wurde:

:bar.object_id == :bar.hash # => true in Ruby 1.8.7

In Ruby 1.9.x hat sich dies geändert, da sich die Hashes von einer Sitzung zur nächsten ändern (einschließlich der von Symbols):

:bar.hash # => some number that will be different next time Ruby 1.9 is ran
Marc-André Lafortune
quelle
+1 für Ihre hervorragenden Noten! Ich habe die Hash-Funktion in meiner Antwort ursprünglich nicht erwähnt, weil ich versucht habe, das Lesen zu vereinfachen :)
Tilo
@ Tilo: in der Tat, deshalb habe ich meine Antwort geschrieben :-) Ich habe gerade meine Antwort bearbeitet, um die spezielle Syntax in Ruby 1.9 und die versprochenen benannten Parameter von Ruby 2.0 zu erwähnen
Marc-André Lafortune
Können Sie erklären, wie Hash-Lookups für Symbole und O (n) für Strings konstant sind?
Asad Moosvi
7

Betreff: Was ist der Vorteil gegenüber der Verwendung einer Zeichenfolge?

  • Styling: Es ist der Ruby-Weg
  • (Sehr) etwas schnellere Wertesuchen, da das Hashing eines Symbols dem Hashing einer Ganzzahl gegenüber dem Hashing einer Zeichenfolge entspricht.

  • Nachteil: Verbraucht einen Steckplatz in der Symboltabelle des Programms, der niemals freigegeben wird.

Larry K.
quelle
4
+1 für die Erwähnung, dass das Symbol niemals Müll gesammelt wird.
Vortico
Das Symbol ist nie Müll gesammelt - nicht wahr seit Ruby 2.2+
Eudaimonia
0

Ich wäre sehr interessiert an einem Follow-up zu eingefrorenen Zeichenfolgen, die in Ruby 2.x eingeführt wurden.

Wenn Sie mit zahlreichen Zeichenfolgen arbeiten, die aus einer Texteingabe stammen (ich denke beispielsweise an HTTP-Parameter oder Nutzdaten über Rack), ist es viel einfacher, Zeichenfolgen überall zu verwenden.

Wenn Sie mit Dutzenden von ihnen zu tun haben, sie sich aber nie ändern (wenn sie Ihr Geschäftsvokabular sind), denke ich gerne, dass das Einfrieren einen Unterschied machen kann. Ich habe noch keinen Benchmark durchgeführt, aber ich denke, es wäre nahe an der Symbolleistung.

jlecour
quelle