Antworten:
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:
Der Grund ist die Effizienz mit mehreren Gewinnen gegenüber einem String:
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 String
Schlü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 :bar
in 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
Betreff: Was ist der Vorteil gegenüber der Verwendung einer Zeichenfolge?
(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.
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.