S'il y a au moins deux instances de la même chaîne dans mon script, dois-je utiliser à la place un symbole?
S'il y a au moins deux instances de la même chaîne dans mon script, dois-je utiliser à la place un symbole?
Réponses:
Une règle empirique simple consiste à utiliser des symboles chaque fois que vous avez besoin d'identifiants internes. Pour Ruby <2.2, n'utilisez les symboles que lorsqu'ils ne sont pas générés dynamiquement, pour éviter les fuites de mémoire.
La seule raison de ne pas les utiliser pour les identifiants générés dynamiquement est à cause de problèmes de mémoire.
Cette question est très courante car de nombreux langages de programmation n'ont pas de symboles, seulement des chaînes, et donc des chaînes sont également utilisées comme identificateurs dans votre code. Vous devriez vous soucier de ce que les symboles sont censés être , pas seulement quand vous devez utiliser des symboles . Les symboles sont censés être des identificateurs. Si vous suivez cette philosophie, il y a de fortes chances que vous fassiez les choses correctement.
Il existe plusieurs différences entre l'implémentation des symboles et des chaînes. La chose la plus importante à propos des symboles est qu'ils sont immuables . Cela signifie qu'ils ne verront jamais leur valeur modifiée. Pour cette raison, les symboles sont instanciés plus rapidement que les chaînes et certaines opérations telles que la comparaison de deux symboles sont également plus rapides.
Le fait qu'un symbole soit immuable permet à Ruby d'utiliser le même objet à chaque fois que vous faites référence au symbole, ce qui économise de la mémoire. Donc à chaque fois que l'interprète lit:my_key
il peut le prendre de la mémoire au lieu de l'instancier à nouveau. C'est moins coûteux que d'initialiser une nouvelle chaîne à chaque fois.
Vous pouvez obtenir une liste de tous les symboles déjà instanciés avec la commande Symbol.all_symbols
:
symbols_count = Symbol.all_symbols.count # all_symbols is an array with all
# instantiated symbols.
a = :one
puts a.object_id
# prints 167778
a = :two
puts a.object_id
# prints 167858
a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!
puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.
Pour les versions Ruby antérieures à la 2.2, une fois qu'un symbole est instancié, cette mémoire ne sera plus jamais libre . Le seul moyen de libérer de la mémoire est de redémarrer l'application. Les symboles sont donc également une cause majeure de fuites de mémoire lorsqu'ils sont utilisés de manière incorrecte. Le moyen le plus simple de générer une fuite de mémoire est d'utiliser la méthode to_sym
sur les données d'entrée utilisateur, puisque ces données changeront toujours, une nouvelle partie de la mémoire sera utilisée pour toujours dans l'instance du logiciel. Ruby 2.2 a introduit le ramasse-miettes de symboles , qui libère les symboles générés dynamiquement, donc les fuites de mémoire générées par la création dynamique de symboles ne sont plus un problème.
Répondre à votre question:
Est-il vrai que je dois utiliser un symbole au lieu d'une chaîne s'il y a au moins deux chaînes identiques dans mon application ou mon script?
Si vous recherchez un identifiant à utiliser en interne dans votre code, vous devez utiliser des symboles. Si vous imprimez la sortie, vous devriez utiliser des chaînes, même si elle apparaît plus d'une fois, même en allouant deux objets différents en mémoire.
Voici le raisonnement:
@AlanDert: si j'utilise plusieurs fois quelque chose comme% input {type:: checkbox} dans le code haml, que dois-je utiliser comme case à cocher?
Moi oui.
@AlanDert: Mais pour imprimer un symbole sur une page html, il doit être converti en chaîne, n'est-ce pas? quel est l'intérêt de l'utiliser alors?
Quel est le type d'une entrée? Un identifiant du type d'entrée que vous souhaitez utiliser ou quelque chose que vous souhaitez montrer à l'utilisateur?
Il est vrai que cela deviendra du code HTML à un moment donné, mais au moment où vous écrivez cette ligne de votre code, c'est censé être un identifiant - il identifie le type de champ de saisie dont vous avez besoin. Ainsi, il est utilisé maintes et maintes fois dans votre code, et a toujours la même "chaîne" de caractères que l'identifiant et ne générera pas de fuite mémoire.
Cela dit, pourquoi n'évaluons-nous pas les données pour voir si les chaînes sont plus rapides?
C'est un simple benchmark que j'ai créé pour cela:
require 'benchmark'
require 'haml'
str = Benchmark.measure do
10_000.times do
Haml::Engine.new('%input{type: "checkbox"}').render
end
end.total
sym = Benchmark.measure do
10_000.times do
Haml::Engine.new('%input{type: :checkbox}').render
end
end.total
puts "String: " + str.to_s
puts "Symbol: " + sym.to_s
Trois sorties:
# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68
Donc, utiliser smbols est en fait un peu plus rapide que les chaînes. Pourquoi donc? Cela dépend de la manière dont HAML est implémenté. J'aurais besoin de pirater un peu le code HAML pour voir, mais si vous continuez à utiliser des symboles dans le concept d'identifiant, votre application sera plus rapide et fiable. Lorsque des questions surgissent, comparez-les et obtenez vos réponses.
En termes simples, un symbole est un nom, composé de caractères, mais immuable. Une chaîne, au contraire, est un conteneur ordonné pour les caractères, dont le contenu est autorisé à changer.
Voici un joli benchmark de chaînes vs symboles que j'ai trouvé à la codecademy:
require 'benchmark'
string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]
string_time = Benchmark.realtime do
1000_000.times { string_AZ["r"] }
end
symbol_time = Benchmark.realtime do
1000_000.times { symbol_AZ[:r] }
end
puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."
La sortie est:
String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.
utiliser des symboles comme identificateurs de clé de hachage
{key: "value"}
les symboles vous permettent d'appeler la méthode dans un ordre différent
def write (fichier :, data :, mode: "ascii") # supprimé par souci de concision fin écrire (données: 123, fichier: "test.txt")
freeze pour conserver comme une chaîne et économiser de la mémoire
label = 'My Label'.freeze
/
(aprèsstrings
) du lien. Le voici: www.reactive.io/tips/2009/01/11/the-difference-between-ruby- symboles-and-strings