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 +
?
10 réponses
, Vous pouvez le faire de plusieurs façons:
- Comme vous l'avez montré avec
<<
mais le habitude chemin -
Avec interpolation de chaîne
source = "#{ROOT_DIR}/#{project}/App.config"
-
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.
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"
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')
À 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)
Puisque c'est un chemin, j'utiliserais probablement array et join:
source = [ROOT_DIR, project, 'App.config'] * '/'
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.
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)
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.
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 <<
.
Voici d'autres façons de le faire:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
Et ainsi de suite ...