La concatenación de cadenas en Ruby
-
22-08-2019 - |
Pregunta
Busco una forma más elegante de la concatenación de cadenas en Ruby.
Tengo la siguiente línea:
source = "#{ROOT_DIR}/" << project << "/App.config"
¿Hay una manera más agradable de hacer esto?
Y para el caso de lo que es la diferencia entre <<
y +
?
Solución
Puede hacerlo de varias maneras:
- Como se muestra con
<<
pero ese no es el habitual manera -
Para la interpolación de cadena
source = "#{ROOT_DIR}/#{project}/App.config"
-
con
+
source = "#{ROOT_DIR}/" + project + "/App.config"
El segundo método parece ser más eficiente en términos de memoria / velocidad de lo que he visto (no se mide sin embargo). Los tres métodos generará un error constante sin inicializar cuando ROOT_DIR es nula.
Cuando se trata de nombres de ruta, es posible que desee utilizar File.join
para evitar ensuciar con separador de ruta.
Al final, se trata de una cuestión de gusto.
Otros consejos
El operador +
es la opción normal de la concatenación, y es probablemente la manera más rápida para concatenar cadenas.
La diferencia entre +
y <<
es que <<
cambia el objeto en su mano izquierda, y +
no lo hace.
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 se acaba de concatenando caminos que puede utilizar un método propio File.join de Ruby.
source = File.join(ROOT_DIR, project, 'App.config')
desde http://greyblake.com/blog/2012/ 09/02 / rubí del perfomance-tricks /
El uso de alias <<
concat
es mucho más eficiente que +=
, ya que éste crea un objeto temporal y anula el primer objeto con el nuevo objeto.
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
salida:
____________________________________________________________
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)
Dado que este es un camino que probablemente haría uso de la matriz y de Ingreso:
source = [ROOT_DIR, project, 'App.config'] * '/'
Aquí hay otro punto de referencia inspirado en este GIST . Se compara concatenación (+
), añadiendo (<<
) y la interpolación (#{}
) para cuerdas dinámicas y predefinidos.
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
salida:
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)
Conclusión:. Interpolación en la RM es pesado
Yo prefiero usar nombre de ruta:
require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'
sobre <<
y +
de los documentos del rubí:
+
: Devuelve un nueva cadena que contiene other_str concatenado a STR
<<
: concatena el objeto dado a str. Si el objeto es un Fixnum entre 0 y 255, que se convierte en un personaje antes de concatenación.
por lo diferencia está en lo que se convierte en primer operando (<<
hace cambios en su lugar, +
devuelve nueva cadena por lo que es la memoria más pesado) y lo será si el primer operando es Fixnum (<<
será agregar como si se tratara de carácter con el código de la igualdad a ese número, +
elevará error)
Te voy a enseñar a todos ustedes mi experiencia con eso.
tuve una consulta que devuelve 32k de registros, para cada registro que se llama un método para dar formato a ese registro de base de datos en una cadena con formato y de concatenar que en una cadena que al final de todo este proceso Wil se convierten en un archivo en disco.
Mi problema era que el registro va, alrededor de 24k, el proceso de concatenar la cadena enciende un dolor.
que estaba haciendo que el uso regular de operador '+'.
Cuando cambié a la '<<' era como magia. Fue muy rápido.
Entonces, recordé mis viejos tiempos - clase de 1998 - cuando yo estaba usando Java y la concatenación de cadena usando '+' y cambiado de String a StringBuffer (y ahora, desarrollador de Java tienen el StringBuilder).
Creo que el proceso de + / << en Ruby mundo es el mismo que + / StringBuilder.append en el mundo Java.
La primera reasignar todo el objeto en la memoria y el otro punto justo a una nueva dirección.
La concatenación se dice? ¿Qué tal método #concat
entonces?
a = 'foo'
a.object_id #=> some number
a.concat 'bar' #=> foobar
a.object_id #=> same as before -- string a remains the same object
En toda la imparcialidad, concat
es alias como <<
.
Aquí hay más maneras de hacer esto:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
Y así sucesivamente ...
Es posible utilizar +
u operador <<
, pero en función de rubí .concat
es el más preferible uno, ya que es mucho más rápido que otros operadores. Se puede utilizar como.
source = "#{ROOT_DIR}/".concat(project.concat.("/App.config"))
También puede utilizar %
de la siguiente manera:
source = "#{ROOT_DIR}/%s/App.config" % project
Este enfoque funciona con '
marca (individual) cita también.
asuntos situación, por ejemplo:
# 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
En el primer ejemplo, la concatenación con el operador +
no actualizará el objeto output
, sin embargo, en el segundo ejemplo, el operador <<
actualizará el objeto output
con cada iteración. Por lo tanto, para el mencionado tipo de situación, <<
es mejor.
Puede concatenar en la definición cadena directamente:
nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"