Concaténation de chaîne en Ruby

Je cherche une façon plus élégante de concaténer des chaînes en Ruby.

J'ai la ligne suivante:

source = "#{ROOT_DIR}/" << project << "/App.config"

Y a-t-il une meilleure façon de faire cela?

Et d'ailleurs quelle est la différence entre << et +?

310
demandé sur idmean 2008-12-18 16:04:21

10 réponses

, Vous pouvez le faire de plusieurs façons:

  1. Comme vous l'avez montré avec << mais le habitude chemin
  2. Avec interpolation de chaîne

    source = "#{ROOT_DIR}/#{project}/App.config"
    
  3. Avec +

    source = "#{ROOT_DIR}/" + project + "/App.config"
    

La deuxième méthode semble être plus efficace en termes de mémoire/vitesse d'après ce que j'ai vu (pas mesuré cependant). Les trois méthodes lanceront une erreur constante non initialisée lorsque ROOT_DIR est nul.

Lorsque vous traitez des noms de chemin, vous pouvez utiliser File.join pour éviter de gâcher avec le séparateur de nom de chemin.

En fin de Compte, c'est une question de goût.

492
répondu Keltia 2016-01-01 20:14:57

L'opérateur + est le choix de concaténation normal, et est probablement le moyen le plus rapide de concaténer des chaînes.

La différence entre + et << que << modifications de l'objet sur son côté gauche, et + ne l'est pas.

irb(main):001:0> s = 'a'
=> "a"
irb(main):002:0> s + 'b'
=> "ab"
irb(main):003:0> s
=> "a"
irb(main):004:0> s << 'b'
=> "ab"
irb(main):005:0> s
=> "ab"
83
répondu Matt Burke 2012-06-27 02:52:15

Si vous concaténez simplement des chemins, vous pouvez utiliser le propre fichier de Ruby.méthode de participation.

source = File.join(ROOT_DIR, project, 'App.config')
74
répondu georg 2008-12-18 13:22:32

À Partir de http://greyblake.com/blog/2012/09/02/ruby-perfomance-tricks/

Utiliser << aka concat est beaucoup plus efficace que +=, car ce dernier crée un objet temporel et remplace le premier objet par le nouvel objet.

require 'benchmark'

N = 1000
BASIC_LENGTH = 10

5.times do |factor|
  length = BASIC_LENGTH * (10 ** factor)
  puts "_" * 60 + "\nLENGTH: #{length}"

  Benchmark.bm(10, '+= VS <<') do |x|
    concat_report = x.report("+=")  do
      str1 = ""
      str2 = "s" * length
      N.times { str1 += str2 }
    end

    modify_report = x.report("<<")  do
      str1 = "s"
      str2 = "s" * length
      N.times { str1 << str2 }
    end

    [concat_report / modify_report]
  end
end

Sortie:

____________________________________________________________
LENGTH: 10
                 user     system      total        real
+=           0.000000   0.000000   0.000000 (  0.004671)
<<           0.000000   0.000000   0.000000 (  0.000176)
+= VS <<          NaN        NaN        NaN ( 26.508796)
____________________________________________________________
LENGTH: 100
                 user     system      total        real
+=           0.020000   0.000000   0.020000 (  0.022995)
<<           0.000000   0.000000   0.000000 (  0.000226)
+= VS <<          Inf        NaN        NaN (101.845829)
____________________________________________________________
LENGTH: 1000
                 user     system      total        real
+=           0.270000   0.120000   0.390000 (  0.390888)
<<           0.000000   0.000000   0.000000 (  0.001730)
+= VS <<          Inf        Inf        NaN (225.920077)
____________________________________________________________
LENGTH: 10000
                 user     system      total        real
+=           3.660000   1.570000   5.230000 (  5.233861)
<<           0.000000   0.010000   0.010000 (  0.015099)
+= VS <<          Inf 157.000000        NaN (346.629692)
____________________________________________________________
LENGTH: 100000
                 user     system      total        real
+=          31.270000  16.990000  48.260000 ( 48.328511)
<<           0.050000   0.050000   0.100000 (  0.105993)
+= VS <<   625.400000 339.800000        NaN (455.961373)
19
répondu Danny 2016-07-01 13:02:46

Puisque c'est un chemin, j'utiliserais probablement array et join:

source = [ROOT_DIR, project, 'App.config'] * '/'
9
répondu Dejan Simic 2008-12-18 13:14:23

Voici un autre benchmark inspiré par cet essentiel . Il compare la concaténation (+), l'ajout (<<) et l'interpolation (#{}) pour les chaînes dynamiques et prédéfinies.

require 'benchmark'

# we will need the CAPTION and FORMAT constants:
include Benchmark

count = 100_000


puts "Dynamic strings"

Benchmark.benchmark(CAPTION, 7, FORMAT) do |bm|
  bm.report("concat") { count.times { 11.to_s +  '/' +  12.to_s } }
  bm.report("append") { count.times { 11.to_s << '/' << 12.to_s } }
  bm.report("interp") { count.times { "#{11}/#{12}" } }
end


puts "\nPredefined strings"

s11 = "11"
s12 = "12"
Benchmark.benchmark(CAPTION, 7, FORMAT) do |bm|
  bm.report("concat") { count.times { s11 +  '/' +  s12 } }
  bm.report("append") { count.times { s11 << '/' << s12 } }
  bm.report("interp") { count.times { "#{s11}/#{s12}"   } }
end

Sortie:

Dynamic strings
              user     system      total        real
concat    0.050000   0.000000   0.050000 (  0.047770)
append    0.040000   0.000000   0.040000 (  0.042724)
interp    0.050000   0.000000   0.050000 (  0.051736)

Predefined strings
              user     system      total        real
concat    0.030000   0.000000   0.030000 (  0.024888)
append    0.020000   0.000000   0.020000 (  0.023373)
interp    3.160000   0.160000   3.320000 (  3.311253)

Conclusion: l'interpolation en IRM est lourde.

7
répondu Adobe 2017-02-13 18:06:02

Je préfère utiliser Pathname:

require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'

À propos de << et + à partir de documents ruby:

+: renvoie une chaînenew contenant other_str concaténée en str

<<: concatène l'objet donné à str. Si l'objet est un Fixnum entre 0 et 255, Il est converti en un caractère avant la concaténation.

Donc la différence est dans ce qui devient le premier opérande (<< fait des changements en place, + renvoie une nouvelle chaîne de sorte que la mémoire est plus lourde) et ce qui sera si le premier opérande est Fixnum (<< ajoutera comme s'il s'agissait d'un caractère avec un code égal à ce nombre, + déclenchera une erreur)

6
répondu tig 2012-06-27 02:53:14

Laissez-moi vous montrer toute mon expérience avec ça.

J'ai eu une requête qui a renvoyé 32k d'enregistrements, pour chaque enregistrement j'ai appelé une méthode pour formater cet enregistrement de base de données en une chaîne formatée et que concaténer cela en une chaîne qui à la fin de tout ce processus se transformera en un fichier sur le disque.

Mon problème était que par le dossier va, autour de 24k, le processus de concaténation de la chaîne a tourné sur une douleur.

Je le faisais en utilisant le '+'régulier opérateur.

Quand j'ai changé pour le '

Donc, je me suis souvenu de mes vieux temps-une sorte de 1998 - quand J'utilisais Java et que je concaténaisais la chaîne en utilisant ' + ' et que je passais de String à StringBuffer(et maintenant nous, développeur Java, avons le StringBuilder).

Je crois que le processus de + /

Le premier réalloue l'objet entier en mémoire et l'autre pointe juste vers une nouvelle adresse.

6
répondu Marcio Mangar 2012-08-03 20:11:33

Concaténation dites-vous? Que diriez-vous de la méthode #concat alors?

a = 'foo'
a.object_id #=> some number
a.concat 'bar' #=> foobar
a.object_id #=> same as before -- string a remains the same object

En toute justice, {[2] } est Alias <<.

5
répondu Boris Stitnicky 2013-06-12 03:48:00

Voici d'autres façons de le faire:

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

Et ainsi de suite ...

5
répondu Imran Alavi 2013-09-25 20:50:49