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:
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.
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.
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.
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.
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.
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".
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.
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
etpatient2
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.]
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
.
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.