Je travaille à travers Ruby Koans.
Le test_the_shovel_operator_modifies_the_original_string
Koan dans about_strings.rb inclut le commentaire suivant:
Les programmeurs Ruby ont tendance à privilégier l'opérateur de pelle (<<) à l'opérateur plus égal (+ =) lors de la création de chaînes. Pourquoi?
Je suppose que cela implique de la vitesse, mais je ne comprends pas l'action sous le capot qui rendrait l'opérateur de la pelle plus rapide.
Quelqu'un pourrait-il expliquer les détails de cette préférence?
Preuve:
a = 'foo'
a.object_id #=> 2154889340
a << 'bar'
a.object_id #=> 2154889340
a += 'quux'
a.object_id #=> 2154742560
<<
Modifie donc la chaîne d'origine plutôt que d'en créer une nouvelle. La raison en est que dans Ruby a += b
Est un raccourci syntaxique pour a = a + b
(Il en va de même pour les autres opérateurs <op>=
) Qui est Par contre, <<
est un alias de concat()
qui modifie le récepteur en place.
Preuve de performance:
#!/usr/bin/env Ruby
require 'benchmark'
Benchmark.bmbm do |x|
x.report('+= :') do
s = ""
10000.times { s += "something " }
end
x.report('<< :') do
s = ""
10000.times { s << "something " }
end
end
# Rehearsal ----------------------------------------
# += : 0.450000 0.010000 0.460000 ( 0.465936)
# << : 0.010000 0.000000 0.010000 ( 0.009451)
# ------------------------------- total: 0.470000sec
#
# user system total real
# += : 0.270000 0.010000 0.280000 ( 0.277945)
# << : 0.000000 0.000000 0.000000 ( 0.003043)
Un ami qui apprend Ruby comme son premier langage de programmation m'a posé cette même question en parcourant les chaînes dans Ruby sur la série Ruby Koans. Je lui ai expliqué cela en utilisant l'analogie suivante;
Vous avez un verre d'eau à moitié plein et vous devez remplir votre verre.
Pour commencer, prenez un nouveau verre, remplissez-le à moitié avec de l'eau du robinet, puis utilisez ce deuxième verre à moitié plein pour remplir votre verre à boire. Vous faites cela à chaque fois que vous devez remplir votre verre.
La deuxième façon de prendre votre verre à moitié plein et de le remplir avec de l'eau directement du robinet.
À la fin de la journée, vous auriez plus de verres à nettoyer si vous choisissez de choisir un nouveau verre chaque fois que vous avez besoin de remplir votre verre.
Il en va de même pour l'opérateur de la pelle et l'opérateur plus égal. De plus, l'opérateur égal choisit un nouveau "verre" chaque fois qu'il a besoin de remplir son verre tandis que l'opérateur de la pelle prend simplement le même verre et le remplit. À la fin de la journée, plus de collecte de "verre" pour l'opérateur Plus égal.
C'est une vieille question, mais je viens de la parcourir et je ne suis pas entièrement satisfait des réponses existantes. Il y a beaucoup de bons points à propos de la pelle << étant plus rapide que la concaténation + =, mais il y a aussi une considération sémantique.
La réponse acceptée de @noodl montre que << modifie l'objet existant en place, tandis que + = crée un nouvel objet. Vous devez donc déterminer si vous souhaitez que toutes les références à la chaîne reflètent la nouvelle valeur, ou si vous souhaitez laisser les références existantes seules et créer une nouvelle valeur de chaîne à utiliser localement. Si vous avez besoin que toutes les références reflètent la valeur mise à jour, vous devez utiliser <<. Si vous souhaitez laisser d'autres références seules, vous devez utiliser + =.
Un cas très courant est qu'il n'y a qu'une seule référence à la chaîne. Dans ce cas, la différence sémantique n'a pas d'importance et il est naturel de préférer << à cause de sa vitesse.
Parce que c'est plus rapide/ne crée pas une copie de la chaîne <-> garbage collector n'a pas besoin de s'exécuter.
Alors que la majorité des réponses couvrent +=
est plus lent car il crée une nouvelle copie, il est important de garder à l'esprit que +=
et <<
ne sont pas interchangeables! Vous souhaitez utiliser chacun dans des cas différents.
En utilisant <<
modifiera également toutes les variables pointées vers b
. Ici, nous mutons également a
lorsque nous ne le souhaitons pas.
2.3.1 :001 > a = "hello"
=> "hello"
2.3.1 :002 > b = a
=> "hello"
2.3.1 :003 > b << " world"
=> "hello world"
2.3.1 :004 > a
=> "hello world"
Car +=
fait une nouvelle copie, il laisse également inchangées toutes les variables qui le pointent.
2.3.1 :001 > a = "hello"
=> "hello"
2.3.1 :002 > b = a
=> "hello"
2.3.1 :003 > b += " world"
=> "hello world"
2.3.1 :004 > a
=> "hello"
Comprendre cette distinction peut vous faire économiser beaucoup de maux de tête lorsque vous avez affaire à des boucles!
Bien que ce ne soit pas une réponse directe à votre question, pourquoi The Fully Upturned Bin a toujours été l'un de mes articles préférés Ruby. Il contient également des informations sur les chaînes en ce qui concerne collecte des ordures.