Souvent, les gens utilisent des symboles comme clés dans un hash Ruby.
Quel est l'avantage par rapport à l'utilisation d'une chaîne?
Par exemple.:
hash[:name]
vs.
hash['name']
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 fois.
Les symboles Ruby sont immuables (ne peuvent pas être modifiés), ce qui facilite grandement la recherche.
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 fois.
Les symboles dans Ruby sont en gros "chaînes immuables" .. cela signifie qu'ils ne peuvent pas être changés, et cela implique que Ce 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 changées à tout moment. Cela implique que Ruby doit stocker chaque chaîne mentionnée dans le code source dans une entité distincte, par exemple. si vous avez une chaîne "nom" mentionnée plusieurs fois dans votre code source, Ruby doit stocker tous ces éléments dans des objets String distincts, car ils pourraient changer ultérieurement (c'est la nature d'un Ruby chaîne).
Si vous utilisez une chaîne en tant que clé de hachage, Ruby doit évaluer la chaîne et examiner son contenu (et calculer une fonction de hachage sur celle-ci) et comparer le résultat aux valeurs (hachées) des clés sont déjà stockés dans le hachage.
Si vous utilisez un symbole comme clé de hachage, il est implicite qu'il soit immuable. Ruby peut donc simplement comparer la (fonction de hachage) de l'id d'objet à l'ID (de hachage) de clés déjà stockées dans le hachage. (Plus vite)
Inconvénient: Chaque symbole occupe un emplacement dans la table des symboles de l'interprète Ruby, qui n'est jamais libérée. Les symboles ne sont jamais ramassés. Un coin de cas se présente donc lorsque vous avez un grand nombre de symboles (par exemple, ceux générés automatiquement). Dans ce cas, vous devez évaluer l'impact sur la taille de votre interprète Ruby.
Notes:
Si vous effectuez des comparaisons de chaînes, Ruby peut comparer des symboles uniquement par leurs ID 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 hash MD5. Ensuite, Ruby compare ces "clés hachées" les unes aux autres.
Réponse longue:
http://www.reactive.io/tips/2009/01/11/the-difference-between-Ruby-symbols-and-strings
http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-Ruby-symbol
La raison en est l'efficacité, avec des gains multiples 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 uniquement pour le hachage avec des clés de symboles (par exemple, h.merge(foo: 42, bar: 6)
), et Ruby 2.0 a arguments mot-clé qui ne fonctionne que pour touches de symbole.
Notes :
1) Vous serez peut-être surpris d'apprendre que Ruby traite les clés String
différemment des autres types. Effectivement:
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, il était déconseillé de créer constamment de nouveaux Symbols
qui ne seraient jamais réutilisés, car ils resteraient indéfiniment dans la table de recherche Symbol. Ruby 2.2 va les ramasser, alors pas d'inquiétude.
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é avec le changement de hachages 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 par rapport à l'utilisation d'une chaîne?
(Très) des recherches de valeurs légèrement plus rapides car hacher un symbole équivaut à hacher un entier ou hacher 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 gelé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’il s’agit du "vocabulaire" de votre entreprise), j’aime penser que le fait de les geler peut faire la différence. Je n'ai pas encore fait de benchmark, mais j'imagine que ce serait une performance proche des symboles.