Réponses:
TL; DR:
L'utilisation de symboles permet non seulement de gagner du temps lors des comparaisons, mais également d'économiser de la mémoire, car ils ne sont stockés qu'une seule fois.
Les symboles Ruby sont immuables (ne peuvent pas être modifiés), ce qui facilite grandement la recherche de quelque chose
Réponse courte (ish):
L'utilisation de symboles permet non seulement de gagner du temps lors des comparaisons, mais également d'économiser de la mémoire, car ils ne sont stockés qu'une seule fois.
Les symboles dans Ruby sont essentiellement des "chaînes immuables" .. cela signifie qu'ils ne peuvent pas être modifiés, et cela implique que le même symbole lorsqu'il est référencé plusieurs fois dans votre code source, est toujours stocké sous la même entité, par exemple a le même identifiant d'objet .
Les chaînes en revanche sont mutables , elles peuvent être modifiées à tout moment. Cela implique que Ruby doit stocker chaque chaîne que vous mentionnez dans votre code source dans son entité distincte, par exemple si vous avez une chaîne "nom" plusieurs fois mentionnée dans votre code source, Ruby doit toutes les stocker dans des objets String séparés, car ils peut changer plus tard (c'est la nature d'une chaîne Ruby).
Si vous utilisez une chaîne comme clé de hachage, Ruby doit évaluer la chaîne et regarder son contenu (et calculer une fonction de hachage à ce sujet) et comparer le résultat aux valeurs (hachées) des clés qui sont déjà stockées dans le hachage. .
Si vous utilisez un symbole comme clé de hachage, il est implicite qu'il est immuable, donc Ruby peut simplement faire une comparaison de la (fonction de hachage de) object-id avec les object-ids (hachés) des clés qui sont déjà stockées dans le Hash. (Plus vite)
Inconvénient: chaque symbole consomme un emplacement dans la table des symboles de l'interpréteur Ruby, qui n'est jamais libéré. Les symboles ne sont jamais récupérés. Ainsi, un cas d'angle est lorsque vous avez un grand nombre de symboles (par exemple, ceux générés automatiquement). Dans ce cas, vous devez évaluer comment cela affecte la taille de votre interpréteur Ruby.
Remarques:
Si vous faites des comparaisons de chaînes, Ruby peut comparer des symboles uniquement par leurs identifiants d'objet, sans avoir à les évaluer. C'est beaucoup plus rapide que de comparer des chaînes, qui doivent être évaluées.
Si vous accédez à un hachage, Ruby applique toujours une fonction de hachage pour calculer une "clé de hachage" à partir de la clé que vous utilisez. Vous pouvez imaginer quelque chose comme un hachage MD5. Et puis Ruby compare ces "clés hachées" les unes aux autres.
Longue réponse:
La raison est l'efficacité, avec plusieurs gains sur une chaîne:
O(n)
pour les chaînes et constante pour les symboles.De plus, Ruby 1.9 a introduit une syntaxe simplifiée juste pour le hachage avec des clés de symboles (par exemple h.merge(foo: 42, bar: 6)
), et Ruby 2.0 a des arguments de mots clés qui ne fonctionnent que pour les clés de symboles.
Remarques :
1) Vous pourriez être surpris d'apprendre que Ruby traite les String
clés différemment de tout autre type. En effet:
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
Pour les clés de chaîne uniquement, Ruby utilisera une copie figée au lieu de l'objet lui-même.
2) Les lettres "b", "a" et "r" ne sont stockées qu'une seule fois pour toutes les occurrences de :bar
dans un programme. Avant Ruby 2.2, c'était une mauvaise idée de créer constamment de nouveaux Symbols
qui n'étaient jamais réutilisés, car ils resteraient pour toujours dans la table de recherche globale de Symbol. Ruby 2.2 les ramassera, donc pas de soucis.
3) En fait, le calcul du hachage pour un symbole n'a pas pris de temps dans Ruby 1.8.x, car l'ID d'objet était utilisé directement:
:bar.object_id == :bar.hash # => true in Ruby 1.8.7
Dans Ruby 1.9.x, cela a changé à mesure que les hachages changent d'une session à l'autre (y compris ceux de Symbols
):
:bar.hash # => some number that will be different next time Ruby 1.9 is ran
Re: quel est l'avantage d'utiliser une chaîne?
Des recherches de valeurs (très) légèrement plus rapides car le hachage d'un symbole équivaut au hachage d'un entier par rapport au hachage d'une chaîne.
Inconvénient: consomme un emplacement dans la table des symboles du programme qui n'est jamais libéré.
Je serais très intéressé par un suivi concernant les chaînes figées introduites dans Ruby 2.x.
Lorsque vous traitez de nombreuses chaînes provenant d'une entrée de texte (je pense aux paramètres HTTP ou à la charge utile, via Rack, par exemple), il est beaucoup plus facile d'utiliser des chaînes partout.
Lorsque vous traitez avec des dizaines d'entre eux mais qu'ils ne changent jamais (s'ils sont le «vocabulaire» de votre entreprise), j'aime à penser que les geler peut faire une différence. Je n'ai pas encore fait de benchmark, mais je suppose que ce serait proche de la performance des symboles.