concaténation de chaîne en Ruby
-
22-08-2019 - |
Question
Je cherche une façon plus élégante de cordes concaténer en Ruby.
J'ai la ligne suivante:
source = "#{ROOT_DIR}/" << project << "/App.config"
Y at-il une plus belle façon de le faire?
Et d'ailleurs quelle est la différence entre <<
et +
?
La solution
Vous pouvez le faire de plusieurs façons:
- Comme vous montré avec
<<
mais ce n'est pas le façon habituelle -
Avec l'interpolation de chaîne
source = "#{ROOT_DIR}/#{project}/App.config"
-
avec
+
source = "#{ROOT_DIR}/" + project + "/App.config"
La seconde méthode semble être plus efficace en terme de mémoire / vitesse de ce que j'ai vu (ne se mesure pas bien). Les trois méthodes jetteront une erreur constante lorsque non initialisée ROOT_DIR est nul.
Lorsque vous traitez avec noms de fichiers, vous pouvez utiliser File.join
pour éviter de déconner avec séparateur de chemin.
En fin de compte, il est une question de goût.
Autres conseils
L'opérateur +
est le choix de concaténation normale, et est probablement le meilleur moyen de concaténer des chaînes.
La différence entre +
et <<
est que <<
change l'objet sur son côté gauche, et +
ne fonctionne 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 êtes des chemins concaténez vous pouvez utiliser propre méthode File.join Ruby.
source = File.join(ROOT_DIR, project, 'App.config')
de http://greyblake.com/blog/2012/ 09/02 / ruby-perfomance-tours /
Utilisation <<
aka concat
est beaucoup plus efficace que +=
, que celui-ci crée un objet temporel et remplace le premier objet avec 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 que je serais probablement utiliser le tableau et nous rejoindre:
source = [ROOT_DIR, project, 'App.config'] * '/'
Voici une autre référence inspirée par ce point essentiel . Il compare concaténation (+
), ajout (<<
) et interpolation (#{}
) pour les chaînes et dynamiques prédéfinis.
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:. Interpolation en IRM est lourde
Je préfère utiliser Pathname:
require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'
à propos <<
et +
de docs rubis:
+
: Retourne nouveau Chaîne contenant other_str concaténé à 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 concaténation.
différence est dans ce qui devient de premier opérande (<<
apporte des modifications en place, +
retourne nouvelle chaîne il est donc mémoire plus lourd) et ce sera si le premier opérande est Fixnum (<<
ajoutera comme si elle était de caractère avec le code égal à ce numéro, +
soulèvera erreur)
Laissez-moi vous montrer à toute mon expérience avec cela.
J'ai eu une requête qui a renvoyé 32k des dossiers, pour chaque enregistrement que j'ai appelé une méthode pour formater cet enregistrement de base de données dans une chaîne formatée et que concaténer que dans une chaîne à la fin de tout ce processus wil se transformer en un fichier disque.
Mon problème était que le dossier va, autour de 24k, le processus de concaténer la chaîne activée une douleur.
Je faisais cela en utilisant l'opérateur '+' régulier.
Quand j'ai changé le « << » était comme par magie. Était vraiment rapide.
Alors, je me suis rappelé mes vieux temps - sorte de 1998 - quand j'utilisais Java et concaténer String à l'aide « + » et changé de chaîne à StringBuffer (et maintenant nous, développeur Java ont StringBuilder).
Je crois que le processus de + / << dans le monde Ruby est le même que + / StringBuilder.append dans le monde Java.
La première réallouer l'objet entier en mémoire et l'autre point seulement à une nouvelle adresse.
Enchaînement vous dites? 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 équité, concat
est Lissé comme <<
.
Voici d'autres façons de le faire:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
Et ainsi de suite ...
Vous pouvez utiliser +
ou opérateur <<
, mais en fonction de .concat
rubis est la plus préférable, car il est beaucoup plus rapide que d'autres opérateurs. Vous pouvez l'utiliser comme.
source = "#{ROOT_DIR}/".concat(project.concat.("/App.config"))
Vous pouvez également utiliser %
comme suit:
source = "#{ROOT_DIR}/%s/App.config" % project
Cette approche fonctionne avec guillemet '
(unique), ainsi.
questions de situation, par exemple:
# this will not work
output = ''
Users.all.each do |user|
output + "#{user.email}\n"
end
# the output will be ''
puts output
# this will do the job
output = ''
Users.all.each do |user|
output << "#{user.email}\n"
end
# will get the desired output
puts output
Dans le premier exemple, concaténer avec l'opérateur +
mettra pas à jour l'objet output
, cependant, dans le second exemple, l'opérateur <<
mettra à jour l'objet output
à chaque itération. Donc, pour le type de situation ci-dessus, <<
est mieux.
Vous pouvez concaténer dans la définition de chaîne directement:
nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"