Malgré la lecture de " Understanding 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, dont deux contenus dans des objets différents, existe dans le même emplacement mémoire, comment se fait-il qu'ils contiennent des valeurs différentes ? Je m'attendais à ce que le même emplacement mémoire contienne la même valeur.
Voici 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.
Prenons 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 tous les deux des hachages, c'est très bien. :ruby
cependant est un symbole. Si nous devions produire ce qui suit:
patient1.each_key {|key| puts key.to_s}
Alors qu'est-ce qui sera produit? "red"
, ou "programming"
?
Oubliant les hachages pendant une seconde, je pense qu'un symbole est un pointeur vers une valeur. Les questions que j'ai sont:
- Puis-je attribuer une valeur à un symbole?
- Un symbole est-il juste un pointeur vers une variable contenant une valeur?
- Si les symboles sont globaux, cela signifie-t-il qu'un symbole pointe toujours vers une chose?
puts patient1[:ruby]
, il imprimera "rouge", si vous ditesputs patient2[:ruby]
, il imprimera "programmation".Réponses:
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 manière dont vous créez un objet symbole, tant que son contenu est le même, il fera référence au même objet en mémoire. Ce n'est pas un problème car un symbole est un objet immuable . Les chaînes 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.
la source
Hash
(créé par {... => ...} dans votre code) qui stocke les paires clé / valeur, pas lesSymbol
s eux-mêmes. LesSymbol
s (par exemple:symbol
ou:sym
ou:ruby
) sont les clés des paires. Ce n'est que dans le cadre d'unHash
"point" vers quoi que ce soit.J'étais capable de grogner des symboles quand j'y pensais 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é, toutes ces méthodes supplémentaires ne sont pas utilisées. Ils ont donc créé des symboles, qui sont des objets de chaîne avec toutes les fonctionnalités supprimées, sauf ce qui est nécessaire pour que ce soit une bonne clé.
Pensez simplement aux symboles comme des chaînes constantes.
la source
Le symbole
:ruby
ne contient pas"red"
ou"programming"
. Le symbole:ruby
n'est que le symbole:ruby
. Ce sont vos hachages,patient1
etpatient2
que chacun contient ces valeurs, dans chaque cas pointé 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 sur elles qui disent "Kezzer". On a des chaussettes dedans et l'autre a du charbon. Vous n'allez pas être confus et demander comment "Kezzer" peut contenir à la fois des chaussettes et du charbon, même s'il porte le même nom. Parce que le nom ne contient pas les cadeaux (merdiques). Il ne fait que les pointer du doigt. De même,
:ruby
ne contient pas les valeurs dans votre hachage, il pointe simplement vers elles.la source
mystring = :steveT
le symbole ne pointe vers rien. Une clé dans un hachage a une valeur associée et la clé peut être un symbole. Mais un symbole n'a pas besoin d'être dans un hachage.Vous pourriez supposer que la déclaration que vous avez faite définit la valeur d'un symbole comme étant autre chose que ce qu'il 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 c'est plus efficace que de gérer un grand nombre de chaînes de longueur variable.
Prenons le cas de votre exemple:
patient1 = { :ruby => "red" }
Cela doit être lu comme suit: "déclarer une variable patient1 et la définir comme un Hash, et dans ce magasin la valeur 'red' 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 mission, il n'est guère surprenant que le résultat obtenu soit identique à ce que vous lui avez attribué en premier lieu.
Le concept de symbole peut être un peu déroutant car ce 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 mappe des valeurs identiques sur le 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 de symbole comme étant tirées d'une table de hachage interne et vous pouvez voir toutes les valeurs qui ont été encodé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=, ...
Au fur et à mesure que vous définissez de nouveaux symboles par la notation deux-points ou en utilisant .to_sym, cette table grandira.
la source
Les symboles ne sont pas des pointeurs. Ils ne contiennent pas de valeurs. Les symboles le sont tout simplement .
:ruby
est le symbole:ruby
et c'est tout ce qu'il y a à faire. Il ne contient pas de valeur, il ne fait rien, il existe juste comme symbole:ruby
. Le symbole:ruby
est une valeur comme le chiffre 1. Il ne pointe pas plus vers une autre valeur que le chiffre 1.la source
Ni l'un ni l'autre, 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 en entier interne sera toujours la même, et sa "valeur" (lorsqu'elle est convertie en chaîne) sera toujours "ruby".la source
En bref
Les symboles résolvent le problème de la création de représentations immuables et lisibles par l'homme, qui présentent également l'avantage d'être plus simples à rechercher au moment de l'exécution que des chaînes. Pensez-y 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 complexes et imbriquées 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, pour 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 lisibles par l'homme et faciles à résoudre pour le runtime, les symboles sont un complément idéal à un langage dynamique.
Avantages
Étant donné que les symboles sont immuables, ils peuvent être partagés au cours de l'exécution. Si deux instances de hachage ont un besoin lexicographique ou sémantique commun pour un élément rouge, le symbole: rouge utiliserait à peu près la moitié de la mémoire que la chaîne «red» aurait exigée pour deux hachages.
Puisque: le rouge 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, alors que l'utilisation de «rouge» ajoutera un coût de mémoire puisque chaque instance de hachage aurait besoin de stocker la chaîne mutable sur création.
Je ne sais pas comment Ruby implémente réellement les symboles / chaînes, mais il est clair qu'un symbole offre moins de surcharge d'implémentation dans le runtime car il s'agit d'une représentation fixe. Les symboles Plus nécessitent un caractère de moins à taper qu'une chaîne entre guillemets et moins de frappe est la poursuite éternelle des vrais rubisistes.
Sommaire
Avec un symbole comme: rouge, vous obtenez la lisibilité de la représentation sous forme de chaîne avec moins de frais généraux 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.
la source
Je recommanderais de lire l'article de Wikipedia sur les tables de hachage - je pense que cela vous aidera à avoir une idée de ce que
{:ruby => "red"}
cela signifie vraiment.Un autre exercice qui pourrait vous aider à comprendre la situation: réfléchissez
{1 => "red"}
. Sémantiquement, cela ne signifie pas "définir la valeur de1
sur"red"
", ce qui est impossible dans Ruby. Cela signifie plutôt "créer un objet Hash et stocker la valeur"red"
de la clé1
.la source
Ni l'un ni l'autre, bien sûr. La sortie sera
ruby
. Ce que, BTW, vous auriez pu découvrir en moins de temps qu'il ne vous en a fallu pour taper la question, en la tapant simplement dans IRB à la place.Pourquoi serait- ce
red
ouprogramming
? Les symboles s'évaluent toujours par 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. Il n'est pas nécessaire de faire appelto_s
à lui.]la source
Je suis nouveau sur Ruby, mais je pense (espère?) Que c'est une façon simple de voir les choses ...
Un symbole n'est ni une variable ni une constante. Il ne représente pas ou n'indique pas une valeur. Un symbole EST 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-ce le même que ceci:
:hellobuddy
Sauf que vous ne pouvez pas faire, par exemple,: hellobuddy.upcase. C'est la valeur de la chaîne et UNIQUEMENT la valeur de la chaîne.
De même, ceci:
greeting =>"hellobuddy"
Est-ce le même que ceci:
greeting => :hellobuddy
Mais, encore une fois, sans la surcharge de l'objet chaîne.
la source
Une façon simple de comprendre cela est de penser: «Et si j'utilisais une chaîne plutôt qu'un symbole?
patient1 = { "ruby" => "red" } patient2 = { "ruby" => "programming" }
Ce n'est pas du tout déroutant, non? Vous utilisez "ruby" comme clé dans un hachage .
"ruby"
est une chaîne littérale, c'est donc la valeur. L'adresse mémoire, ou pointeur, n'est pas disponible pour vous. Chaque fois que vous appelez"ruby"
, vous créez une nouvelle instance de celui-ci, c'est-à-dire une nouvelle cellule mémoire contenant la même valeur -"ruby"
.Le hachage va alors "quelle est ma valeur de clé? Oh c'est
"ruby"
. Puis mappe cette valeur à" rouge "ou" programmation ". En d'autres termes,:ruby
ne déréférencera pas à"red"
ou"programming"
. Le hachage correspond:ruby
à"red"
ou"programming"
.Comparez cela à si nous utilisons des symboles
patient1 = { :ruby => "red" } patient2 = { :ruby => "programming" }
La valeur de
:ruby
est aussi"ruby"
, effectivement.Pourquoi? Parce que les symboles sont essentiellement des constantes de chaîne . Les constantes n'ont pas plusieurs instances. C'est la même adresse mémoire. Et une adresse mémoire a une certaine valeur, une fois déréférencée. Pour les symboles, le nom du pointeur est le symbole et la valeur déréférencée est une chaîne, qui correspond au nom du symbole, dans ce cas
"ruby"
,.Dans un hachage, vous n'utilisez pas le symbole, le pointeur, mais la valeur déférencée. Vous n'utilisez pas
:ruby
, mais"ruby"
. Le hachage recherche ensuite la clé"ruby"
, la valeur est"red"
ou"programming"
, selon la façon dont vous avez défini le hachage.Le changement de paradigme et le concept à emporter est que la valeur d'un symbole est un concept complètement distinct d'une valeur mappée par un hachage, étant donné une clé de ce hachage.
la source