tl; dr: Y aurait-il une définition indépendante des langues des symboles et une raison de les avoir dans d'autres langues?
Alors, pourquoi le créateur de Ruby a-t-il utilisé le concept de symbols
dans le langage?
Je pose cette question du point de vue d'un programmeur non rubis. J'ai appris beaucoup d'autres langues, et je n'ai trouvé dans aucune d'entre elles la nécessité de préciser si j'avais affaire ou non à ce que Ruby appelle symbols
.
La question principale est la suivante: le concept de symbols
Ruby existe-t-il pour la performance, ou simplement quelque chose qui est nécessaire en raison de la façon dont le langage est écrit?
Un programme dans Ruby serait-il plus léger et / ou plus rapide que son équivalent, disons, Python ou Javascript? Si oui, serait-ce à cause de cela symbols
?
Étant donné que l'une des intentions de Ruby est d'être facile à lire et à écrire pour les humains, ses créateurs n'ont-ils pas pu faciliter le processus de codage en mettant en œuvre ces améliorations dans l'interpréteur lui-même (comme cela pourrait être dans d'autres langues)?
On dirait que tout le monde veut savoir ce que symbols
sont et comment les utiliser, et non pas pourquoi ils sont là en premier lieu.
la source
Réponses:
Le créateur de Ruby, Yukihiro "Matz" Matsumoto, a posté une explication sur la façon dont Ruby a été influencé par Lisp, Smalltalk, Perl (et Wikipedia dit aussi Ada et Eiffel):
Dans tout compilateur, vous allez gérer les identifiants des fonctions, des variables, des blocs nommés, des types, etc. Généralement, vous les stockez dans le compilateur et les oubliez dans l'exécutable produit, sauf lorsque vous ajoutez des informations de débogage.
En Lisp, ces symboles sont des ressources de première classe, hébergées dans différents packages, ce qui signifie que vous pouvez ajouter de nouveaux symboles au moment de l'exécution, les lier à différents types d'objets. Ceci est utile lors de la méta-programmation, car vous pouvez être sûr que vous n'aurez pas de collisions de noms avec d'autres parties du code.
De plus, les symboles sont internés au moment de la lecture et peuvent être comparés par identité, ce qui est un moyen efficace d'avoir de nouveaux types de valeurs (comme des nombres, mais abstraits). Cela aide à écrire du code dans lequel vous utilisez directement des valeurs symboliques, au lieu de définir vos propres types d'énumération soutenus par des entiers. De plus, chaque symbole peut contenir des données supplémentaires. C'est ainsi, par exemple, qu'Emacs / Slime peut attacher des métadonnées d'Emacs directement dans la liste des propriétés d'un symbole.
La notion de symbole est centrale en Lisp. Jetez un œil par exemple à PAIP (Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp, Norvig) pour des exemples détaillés.
la source
Eh bien, ils n'ont pas strictement «dû», ils ont choisi de le faire. Notez également que les
Symbol
s à proprement parler ne font pas partie du langage, ils font partie de la bibliothèque principale. Ils n'ont une syntaxe littérale du niveau de la langue, mais ils travailleraient aussi bien si vous deviez les construire en appelant .Symbol::new
Vous n'avez pas dit ce que sont ces "beaucoup d'autres langues", mais voici juste un petit extrait de langues qui ont un
Symbol
type de données comme Ruby:Il existe également d'autres langages qui fournissent les fonctionnalités de
Symbol
s sous une forme différente. En Java, par exemple, les fonctionnalités de RubyString
sont divisées en deux (en fait trois) types:String
etStringBuilder
/StringBuffer
. D'un autre côté, les fonctionnalités duSymbol
type Ruby sont repliées dans leString
type Java : les JavaString
s peuvent être internés , les chaînes littérales et lesString
s qui sont le résultat d'expressions constantes évaluées au moment de la compilation sont automatiquement internées, lesString
s générés dynamiquement peuvent être internés en appelant laString.intern
méthode. Un internéString
en Java est exactement comme unSymbol
Ruby, mais il n'est pas implémenté comme un type séparé, c'est juste un état différent qu'un JavaString
peut être dans. (Remarque: dans les versions antérieures de Ruby, elleString#to_sym
était appeléeString#intern
et cette méthode existe encore aujourd'hui en tant qu'alias hérité.)Symbol
s sont avant tout un type de données avec une sémantique spécifique . Ces sémantiques permettent également d'implémenter certaines opérations performantes (par exemple le test d'égalité rapide O (1)), mais ce n'est pas le but principal.Symbol
s ne sont pas du tout nécessaires dans le langage Ruby, Ruby fonctionnerait très bien sans eux. Ils sont purement une fonctionnalité de bibliothèque. Il y a exactement un endroit dans le langage qui est lié àSymbol
s: unedef
expression de définition de méthode s'évalue enSymbol
indiquant le nom de la méthode en cours de définition. Cependant, c'est un changement assez récent, avant cela, la valeur de retour était simplement laissée non spécifiée. L'IRM simplement évaluée ànil
, Rubinius évaluée à unRubinius::CompiledMethod
objet, et ainsi de suite. Il serait également possible d'évaluer unUnboundMethod
… ou juste unString
.Je ne suis pas sûr de ce que vous demandez ici. Les performances sont principalement une question de qualité de mise en œuvre, pas de langue. De plus, Node n'est même pas un langage, c'est un cadre d'E / S à événements pour ECMAScript. En exécutant un script équivalent sur IronPython et MRI, IronPython est susceptible d'être plus rapide. Exécuter un script équivalent sur CPython et JRuby + Truffle, JRuby + Truffle est susceptible d'être plus rapide. Cela n'a rien à voir avec
Symbol
s mais avec la qualité de l'implémentation: JRuby + Truffle a un compilateur optimisant de manière agressive, ainsi que toute la machinerie d'optimisation d'une machine virtuelle Java haute performance, CPython est un simple interprète.Les n °
Symbol
ne sont pas une optimisation du compilateur. Il s'agit d'un type de données distinct avec une sémantique spécifique. Ils ne sont pas comme les flonums de YARV , qui sont une optimisation interne privée pour l' alFloat
. La situation est la même que pourInteger
,Bignum
etFixnum
qui devrait être un détail d'optimisation interne privée invisible, mais est malheureusement pas. (Ce va enfin être fixé dans Ruby 2.4, qui enlèveFixnum
etBignum
et feuilles seulementInteger
.)Le faire comme Java le fait, en tant qu'état spécial de
String
s normal signifie que vous devez toujours vous méfier de savoir si vosString
s sont dans cet état spécial et dans quelles circonstances ils sont automatiquement dans cet état spécial et quand ce n'est pas le cas. C'est une charge beaucoup plus élevée que d'avoir simplement un type de données séparé.Symbol
est un type de données qui désigne le concept de nom ou d' étiquette .Symbol
s sont des objets de valeur , immuables, généralement immédiats (si le langage distingue une telle chose), apatrides, et n'ont pas d'identité. DeuxSymbol
s qui sont égaux sont également garantis identiques, en d'autres termes, deuxSymbol
s qui sont égaux sont en fait les mêmesSymbol
. Cela signifie que l'égalité de valeur et l'égalité de référence sont la même chose, et donc l'égalité est efficace et O (1).Les raisons de les avoir dans une langue sont vraiment les mêmes, indépendamment de la langue. Certaines langues en dépendent plus que d'autres.
Dans la famille Lisp, par exemple, il n'y a pas de concept de "variable". Au lieu de cela, vous avez des
Symbol
s associés aux valeurs.Dans les langues avec des capacités de réflexion ou introspectives,
Symbol
s sont souvent utilisés pour désigner les noms des entités reflétées dans les API de réflexion, par exemple dans Ruby,Object#methods
,Object#singleton_methods
,Object#public_methods
,Object#protected_methods
etObject#public_methods
retourner unArray
deSymbol
s (bien qu'ils pourraient tout aussi bien retourner unArray
deMethod
s).Object#public_send
prend unSymbol
dénotant le nom du message à envoyer comme argument (bien qu'il accepte également unString
aussi,Symbol
est plus sémantiquement correct).Dans ECMAScript, les
Symbol
s sont un élément fondamental pour rendre ECMAScript plus sûr à l'avenir. Ils jouent également un grand rôle dans la réflexion.la source
Les symboles sont utiles dans Ruby, et vous les verrez partout dans le code Ruby car chaque symbole est réutilisé chaque fois qu'il est référencé. Il s'agit d'une amélioration des performances par rapport aux chaînes, car chaque utilisation d'une chaîne qui n'est pas enregistrée dans une variable crée un nouvel objet en mémoire. Par exemple, si j'utilise plusieurs fois la même chaîne comme clé de hachage:
La chaîne "a" est créée 101 000 fois en mémoire. Si j'ai utilisé un symbole à la place:
Le symbole
:a
est toujours un objet en mémoire. Cela rend les symboles beaucoup plus efficaces que les chaînes.MISE À JOUR Voici une référence (tirée de Codecademy ) qui démontre la différence de performances:
Voici mes résultats pour mon MBP:
Il y a une différence claire entre l'utilisation de chaînes et de symboles pour simplement identifier les clés dans un hachage.
la source
"a"
est en effet une nouvelle chaîne, je pense que dans votre exemple, il y en aura exactement deux"a"
(et une implémentation pourrait même partager la mémoire jusqu'à ce que l'une d'entre elles soit mutée). Afin de créer des millions de chaînes, vous devrez probablement utiliser String.new ("a"). Mais je ne connais pas bien Ruby, alors peut-être que je me trompe.string_AZ[String.new("r")]
pour voir si cela fait une différence. J'obtiens 21 ms pour les cordes (version originale), 7 ms avec des symboles et 50 ms avec de nouvelles cordes à chaque fois. Je dirais donc que les chaînes ne sont pas allouées autant avec la"r"
version littérale .