web-dev-qa-db-fra.com

Comment comprendre les symboles dans Ruby

Malgré la lecture de " nderstanding Ruby Symbols ", je suis toujours confus par la représentation des données en mémoire quand il s'agit d'utiliser des symboles. Si un symbole, deux de les contiennent dans des objets différents, existent dans le même emplacement mémoire, alors comment se fait-il qu'ils contiennent des valeurs différentes ? Je m'attendais au même emplacement mémoire pour contenir la même valeur.

C'est une citation du lien:

Contrairement aux chaînes, les symboles du même nom sont initialisés et n'existent en mémoire qu'une seule fois lors d'une session de Ruby

Je ne comprends pas comment il parvient à différencier les valeurs contenues dans le même emplacement mémoire.

Considérez cet exemple:

patient1 = { :Ruby => "red" }
patient2 = { :Ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1 et patient2 sont les deux hachages, ça va. :Ruby est cependant un symbole. Si nous devions produire ce qui suit:

patient1.each_key {|key| puts key.to_s}

Alors, quelle sera la sortie? "red", ou "programming"?

Oubliant les hachages pendant une seconde, je pense qu'un symbole est un pointeur vers une valeur. Mes questions sont les suivantes:

  • Puis-je attribuer une valeur à un symbole?
  • Un symbole est-il simplement un pointeur sur une variable contenant une valeur?
  • Si les symboles sont globaux, cela signifie-t-il qu'un symbole pointe toujours vers une chose?
81
Kezzer

Considère ceci:

x = :sym
y = :sym
(x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true

x = "string"
y = "string"
(x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false

Ainsi, quelle que soit la façon dont vous créez un objet symbole, tant que son contenu est le même, il se référera au même objet en mémoire. Ce n'est pas un problème car un symbole est un objet immuable . Les cordes sont mutables.


(En réponse au commentaire ci-dessous)

Dans l'article d'origine, la valeur n'est pas stockée dans un symbole, elle est stockée dans un hachage. Considère ceci:

hash1 = { "string" => "value"}
hash2 = { "string" => "value"}

Cela crée six objets dans la mémoire - quatre objets chaîne et deux objets de hachage.

hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}

Cela ne crée que cinq objets en mémoire - un symbole, deux chaînes et deux objets de hachage.

60
anshul

J'ai pu groquer des symboles quand j'y ai pensé comme ça. Une chaîne Ruby est un objet qui a un tas de méthodes et de propriétés. Les gens aiment utiliser des chaînes pour les clés, et lorsque la chaîne est utilisée pour une clé, alors toutes ces méthodes supplémentaires ne sont pas utilisées Ils ont donc fait des symboles, qui sont des objets chaîne avec toutes les fonctionnalités supprimées, à l'exception de ce qui est nécessaire pour que ce soit une bonne clé.

Considérez simplement les symboles comme des chaînes constantes.

49
Segfault

Le symbole :Ruby ne contient pas "red" ou "programming". Le symbole :Ruby n'est que le symbole :Ruby. Ce sont vos hachages, patient1 et patient2 qui contiennent chacune ces valeurs, dans chaque cas désignées par la même clé.

Pensez-y de cette façon: si vous entrez dans le salon le matin de Noël et que vous voyez deux boîtes avec une étiquette dessus qui disent "Kezzer" dessus. On a des chaussettes et l'autre a du charbon. Vous n'allez pas vous embrouiller et demander comment "Kezzer" peut contenir à la fois des chaussettes et du charbon, même s'il s'agit du même nom. Parce que le nom ne contient pas les cadeaux (merdiques). Il ne fait que les montrer. De même, :Ruby ne contient pas les valeurs de votre hachage, il les pointe simplement vers elles.

32
jcdyer

Vous pourriez supposer que la déclaration que vous avez faite définit la valeur d'un symbole comme autre chose que ce qu'elle est. En fait, un symbole n'est qu'une valeur de chaîne "internalisée" qui reste constante. C'est parce qu'ils sont stockés à l'aide d'un simple identifiant entier qu'ils sont fréquemment utilisés car cela est plus efficace que la gestion d'un grand nombre de chaînes de longueur variable.

Prenons le cas de votre exemple:

patient1 = { :Ruby => "red" }

Cela devrait être lu comme: "déclarer une variable patient1 et la définir comme un Hash, et dans ce magasin la valeur 'rouge' sous la clé (symbole 'Ruby')"

Une autre façon d'écrire ceci est:

patient1 = Hash.new
patient1[:Ruby] = 'red'

puts patient1[:Ruby]
# 'red'

Lorsque vous effectuez une affectation, il n'est pas surprenant que le résultat obtenu soit identique à celui que vous lui avez attribué en premier lieu.

Le concept Symbol peut être un peu déroutant car il n'est pas une caractéristique de la plupart des autres langues.

Chaque objet String est distinct même si les valeurs sont identiques:

[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
  puts v.inspect + ' ' + v.object_id.to_s
end

# "foo" 2148099960
# "foo" 2148099940
# "foo" 2148099920
# "bar" 2148099900
# "bar" 2148099880
# "bar" 2148099860

Chaque symbole avec la même valeur fait référence au même objet:

[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
  puts v.inspect + ' ' + v.object_id.to_s
end

# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668

La conversion de chaînes en symboles associe des valeurs identiques au même symbole unique:

[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
  v = v.to_sym
  puts v.inspect + ' ' + v.object_id.to_s
end

# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668

De même, la conversion de Symbol en String crée une chaîne distincte à chaque fois:

[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
  v = v.to_s
  puts v.inspect + ' ' + v.object_id.to_s
end

# "foo" 2148097820
# "foo" 2148097700
# "foo" 2148097580
# "bar" 2148097460
# "bar" 2148097340
# "bar" 2148097220

Vous pouvez considérer les valeurs des symboles comme tirées d'une table de hachage interne et vous pouvez voir toutes les valeurs qui ont été codées en symboles à l'aide d'un simple appel de méthode:

Symbol.all_values

# => [:Ruby_PATCHLEVEL, :vi_editing_mode, :Separator, :TkLSHFT, :one?, :setuid?, :auto_indent_mode, :setregid, :back, :Fail, :RET, :member?, :TkOp, :AP_NAME, :readbyte, :suspend_context, :oct, :store, :WNOHANG, :@seek, :autoload, :rest, :IN_INPUT, :close_read, :type, :filename_quote_characters=, ...

Lorsque vous définissez de nouveaux symboles soit par la notation deux-points, soit en utilisant .to_sym, ce tableau s'agrandit.

26
tadman

Les symboles ne sont pas des pointeurs. Ils ne contiennent pas de valeurs. Les symboles simplement sont. :Ruby est le symbole :Ruby et c'est tout. Il ne contient pas de valeur, il ne fait rien, il existe simplement en tant que symbole :Ruby. Le symbole :Ruby est une valeur comme le chiffre 1. Il ne pointe pas vers une autre valeur que le nombre 1.

15
Chuck
patient1.each_key {|key| puts key.to_s}

Alors, quelle sera la sortie? "rouge" ou "programmation"?

Ni, il affichera "Ruby".

Vous confondez symboles et hachages. Ils ne sont pas liés, mais ils sont utiles ensemble. Le symbole en question est :Ruby; cela n'a rien à voir avec les valeurs du hachage, et sa représentation interne entière sera toujours la même, et sa "valeur" (lorsqu'elle sera convertie en chaîne) sera toujours "Ruby".

12
meagar

En bref

Les symboles résolvent le problème de la création de représentations lisibles et immuables qui ont également l'avantage d'être plus simples à rechercher pour l'exécution que les chaînes. Considérez-le comme un nom ou une étiquette qui peut être réutilisé.

Pourquoi: le rouge est meilleur que le "rouge"

Dans les langages dynamiques orientés objet, vous créez des structures de données imbriquées complexes avec des références lisibles. Le hachage est un cas d'utilisation courant où vous mappez des valeurs à des clés uniques - uniques, au moins, à chaque instance. Vous ne pouvez pas avoir plus d'une clé "rouge" par hachage.

Cependant, il serait plus efficace pour le processeur d'utiliser un index numérique au lieu de clés de chaîne. Les symboles ont donc été introduits comme un compromis entre vitesse et lisibilité. Les symboles se résolvent beaucoup plus facilement que la chaîne équivalente. En étant lisible par l'homme et facile pour le runtime à résoudre les symboles sont un complément idéal à un langage dynamique.

Avantages

Étant donné que les symboles sont immuables, ils peuvent être partagés sur le runtime. Si deux instances de hachage ont un besoin lexicographique ou sémantique commun pour un élément rouge, le symbole: rouge utiliserait environ la moitié de la mémoire que la chaîne "rouge" aurait exigée pour deux hachages.

Étant donné que: red se résout toujours au même emplacement en mémoire, il peut être réutilisé sur une centaine d'instances de hachage avec presque aucune augmentation de mémoire, tandis que l'utilisation de "red" ajoutera un coût de mémoire puisque chaque instance de hachage devra stocker la chaîne mutable création.

Je ne sais pas comment Ruby implémente réellement les symboles/chaîne mais clairement un symbole offre moins de surcharge d'implémentation dans le runtime car c'est une représentation fixe. De plus, les symboles prennent un caractère de moins à taper qu'une chaîne entre guillemets et moins de frappe est la poursuite éternelle des vrais rubis.

Résumé

Avec un symbole comme: rouge, vous obtenez la lisibilité de la représentation des chaînes avec moins de surcharge en raison du coût des opérations de comparaison de chaînes et de la nécessité de stocker chaque instance de chaîne en mémoire.

10
Mark Fox
patient1 = { :Ruby => "red" }
patient2 = { :Ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1 et patient2 sont les deux hachages, ça va. :Ruby est cependant un symbole. Si nous devions produire ce qui suit:

patient1.each_key {|key| puts key.to_s}

Alors, quelle sera la sortie? "rouge" ou "programmation"?

Non plus, bien sûr. La sortie sera Ruby. BTW, que vous auriez pu découvrir en moins de temps qu'il ne vous a fallu pour taper la question, en la tapant simplement dans IRB à la place.

Pourquoi serait ce serait red ou programming? Les symboles toujours s'évaluent eux-mêmes. La valeur du symbole :Ruby est le symbole :Ruby lui-même et la représentation sous forme de chaîne du symbole :Ruby est la valeur de chaîne "Ruby".

[BTW: puts convertit toujours ses arguments en chaînes, de toute façon. Pas besoin d'appeler to_s dessus.]

3
Jörg W Mittag

Je recommanderais de lire le article Wikipedia sur les tables de hachage - Je pense que cela vous aidera à avoir une idée de ce que {:Ruby => "red"} signifie vraiment.

Un autre exercice qui pourrait vous aider à comprendre la situation: envisagez {1 => "red"}. Sémantiquement, cela ne signifie pas "définir la valeur de 1 à "red" ", ce qui est impossible dans Ruby. Cela signifie plutôt" créer un objet Hash et stocker la valeur "red" pour la clé 1.

3
Greg Campbell

Je suis nouveau chez Ruby, mais je pense (espérons?) Que c'est une façon simple de voir les choses ...

Un symbole n'est pas une variable ou une constante. Il ne remplace ni ne pointe une valeur. Un symbole IS une valeur.

Tout ce que c'est, c'est une chaîne sans la surcharge de l'objet. Le texte et seulement le texte.

Donc ça:

"hellobuddy"

Est la même que celle-ci:

:hellobuddy

Sauf que vous ne pouvez pas faire, par exemple,: hellobuddy.upcase. C'est la valeur de chaîne et UNIQUEMENT la valeur de chaîne.

De même, ceci:

greeting =>"hellobuddy"

Est la même que celle-ci:

greeting => :hellobuddy

Mais, encore une fois, sans la surcharge de l'objet chaîne.

0
Dave Munger