165 votes

Pourquoi utiliser des symboles comme clés de hachage en Ruby ?

Souvent, les gens utilisent des symboles comme clés dans un hachage Ruby.

Quel est l'avantage par rapport à l'utilisation d'une corde ?

Par exemple :

hash[:name]

vs.

hash['name']

233voto

Tilo Points 13833

TL;DR :

L'utilisation de symboles permet non seulement de gagner du temps lors des comparaisons, mais aussi de gagner de la mémoire, car ils ne sont enregistrés qu'une seule 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 aussi de gagner de la mémoire, car ils ne sont enregistrés qu'une seule fois.

Les symboles en Ruby sont En gros, "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é comme la même entité, c'est-à-dire qu'il a le même identifiant d'objet.

Les chaînes de caractères, quant à elles, sont mutables ils peuvent être modifiés à tout moment. Cela implique que Ruby doit stocker chaque chaîne de caractères que vous mentionnez dans votre code source dans une entité distincte. Par exemple, si vous avez une chaîne de caractères "nom" mentionnée plusieurs fois dans votre code source, Ruby doit les stocker dans des objets String distincts, car ils peuvent changer plus tard (c'est la nature d'une chaîne de caractères Ruby).

Si vous utilisez une chaîne de caractères comme clé de hachage, Ruby doit évaluer la chaîne de caractères, regarder son contenu (et calculer une fonction de hachage sur celui-ci) et comparer le résultat avec les 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 l'identifiant de l'objet avec les identifiants (hachés) des clés qui sont déjà stockées dans le hachage. (beaucoup plus rapide)

L'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 collectés à la poubelle. Le cas de figure est donc celui où vous avez un grand nombre de symboles (par exemple, des symboles générés automatiquement). Dans ce cas, vous devez évaluer comment cela affecte la taille de votre interpréteur Ruby.

Notes :

Si vous faites des comparaisons de chaînes de caractères, Ruby peut comparer des symboles simplement par leur identifiant d'objet, sans avoir à les évaluer. C'est beaucoup plus rapide que de comparer des chaînes de caractères, 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. Ensuite, Ruby compare ces "clés de hachage" entre elles.

Longue réponse :

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/

22voto

La raison en est l'efficacité, avec des gains multiples par rapport à une corde :

  1. Les symboles étant immuables, la question "que se passe-t-il si la clé change ?" n'a pas besoin d'être posée.
  2. Les chaînes de caractères sont dupliquées dans votre code et prennent généralement plus de place en mémoire.
  3. Les recherches de hachage doivent calculer le hachage des clés pour les comparer. Il s'agit de O(n) pour les chaînes de caractères et constant pour les symboles.

De plus, Ruby 1.9 a introduit une syntaxe simplifiée uniquement pour les hachages avec des clés de symboles (par ex. h.merge(foo: 42, bar: 6) ), et Ruby 2.0 a arguments de mots-clés qui ne fonctionnent que pour les touches de symbole.

Notes :

1) Vous pourriez être surpris d'apprendre que Ruby traite String différemment de tout autre type de clé. 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 type 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 du mot "b". :bar dans un programme. Avant Ruby 2.2, c'était une mauvaise idée de constamment créer de nouveaux Symbols qui ne sont jamais réutilisés, car ils resteraient à jamais dans la table de consultation globale des symboles. Ruby 2.2 les récupère à la poubelle, donc pas d'inquiétude.

3) En fait, le calcul du hachage d'un symbole ne prenait pas de temps dans Ruby 1.8.x, puisque l'ID de l'objet était utilisé directement :

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

Dans Ruby 1.9.x, cela a changé car les hachages changent d'une session à l'autre (y compris ceux des Symbols ) :

:bar.hash # => some number that will be different next time Ruby 1.9 is ran

7voto

Larry K Points 16266

Re : quel est l'avantage d'utiliser une chaîne de caractères ?

  • Style : c'est le Ruby-way

  • Recherches de valeurs (très) légèrement plus rapides puisque le hachage d'un symbole est équivalent 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é.

0voto

jlecour Points 921

Je serais très intéressé par un suivi concernant les chaînes gelées introduites dans Ruby 2.x.

Lorsque vous avez affaire à de nombreuses chaînes provenant d'une entrée de texte (je pense aux paramètres ou aux données utiles HTTP, via Rack, par exemple), il est beaucoup plus facile d'utiliser des chaînes partout.

Lorsque vous avez affaire à 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 je suppose que les performances des symboles seraient proches.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X