Pourquoi l’opérateur de pelle (<<) est-il préféré à plus-égal (+ =) lors de la construction d’une chaîne dans Ruby?

Je travaille avec Ruby Koans.

Le test_the_shovel_operator_modifies_the_original_ssortingng Koan dans about_ssortingngs.rb inclut le commentaire suivant:

Les programmeurs Ruby ont tendance à privilégier l’opérateur de pelle (<<) sur l’opérateur plus égal à (+ =) lors de la construction des chaînes. Pourquoi?

J’imagine que cela implique de la vitesse, mais je ne comprends pas l’action sous le capot qui ferait que l’opérateur de la pelle soit plus rapide.

Quelqu’un pourrait-il s’il vous plaît expliquer les détails derrière cette préférence?

Preuve:

 a = 'foo' a.object_id #=> 2154889340 a << 'bar' a.object_id #=> 2154889340 a += 'quux' a.object_id #=> 2154742560 

So << modifie 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 (la même chose vaut pour les autres opérateurs = ) qui est une affectation. Par contre << est un alias de concat() qui modifie le récepteur sur 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é la même question en parcourant Ssortingngs in Ruby sur la série Ruby Koans. Je lui ai expliqué en utilisant l’analogie suivante;

Vous avez un verre d’eau à moitié plein et vous devez remplir votre verre.

La première façon de le faire est de prendre un nouveau verre, de le remplir à moitié d’eau d’un robinet puis d’utiliser ce second verre à moitié plein pour remplir votre verre à boire. Vous faites cela chaque fois que vous avez besoin de remplir votre verre.

La deuxième façon de prendre votre verre à moitié plein et de simplement 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 devez remplir votre verre.

La même chose s’applique à l’opérateur de pelle et à l’opérateur plus égal. De plus, un autre opérateur choisit un nouveau «verre» chaque fois qu’il doit remplir son verre tandis que l’opérateur de la pelle prend le même verre et le remplit. En fin de compte, plus de collecte de «verre» pour l’opérateur Plus égal.

C’est une vieille question, mais je viens de la traverser et je ne suis pas entièrement satisfaite 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 souhaitez-vous laisser les références existantes seules et créer une nouvelle valeur de chaîne à utiliser localement. Si vous avez besoin de toutes les références pour refléter la valeur mise à jour, vous devez utiliser <<. Si vous voulez 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 de copie de la chaîne <-> le ramasse-miettes n’a pas besoin d’être exécuté.

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 Ruby préférés. Il contient également des informations sur les chaînes relatives à la récupération de place.