Concatenación de cadenas en Ruby

Estoy buscando una forma más elegante de concatenar 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, ¿cuál es la diferencia entre << y + ?

Puedes hacer eso de varias maneras:

  1. Como se muestra con < < pero esa no es la forma habitual
  2. Con interpolación de cuerdas

     source = "#{ROOT_DIR}/#{project}/App.config" 
  3. 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 (aunque no medido). Los tres métodos generarán un error constante no inicializado cuando ROOT_DIR sea nulo.

Al tratar con nombres de File.join , es posible que desee utilizar File.join para evitar el desorden con el separador de nombre de ruta.

Al final, es una cuestión de gusto.

El operador + es la opción de concatenación normal, y es probablemente la forma más rápida de concatenar cadenas.

La diferencia entre + y < < es que < < cambia el objeto en su lado izquierdo, y + no.

 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 solo está concatenando caminos, puede usar el propio método File.join de Ruby.

 source = File.join(ROOT_DIR, project, 'App.config') 

desde http://greyblake.com/blog/2012/09/02/ruby-perfomance-tricks/

Usar < < aka concat es mucho más eficiente que += , ya que este último 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) 

Como este es un camino, probablemente usaría array y me uniría a:

 source = [ROOT_DIR, project, 'App.config'] * '/' 

Aquí hay otro punto de referencia inspirado en esta esencia . Compara la concatenación ( + ), la adición ( < < ) y la interpolación ( #{} ) para cadenas dinámicas y predefinidas.

 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: la interpolación en MRI es pesada.

Prefiero usar Pathname:

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

sobre < < y + de ruby ​​docs:

+ : Devuelve una 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, se convierte en un carácter antes de la concatenación.

entonces la diferencia está en lo que se convierte en primer operando ( < < hace cambios en su lugar, + devuelve una nueva cadena para que la memoria sea más pesada) y lo que será si el primer operando es Fixnum ( < < se agregará como si fuera un caracter con código igual a ese número, + boostá el error)

Déjame mostrarte toda mi experiencia con eso.

Tenía una consulta que arrojaba 32k de registros, para cada registro llamé a un método para formatear ese registro de base de datos en una cadena formateada y luego lo concateno en una cadena que al final de todo este proceso se convertirá en un archivo en disco.

Mi problema fue que, según el registro, alrededor de 24k, el proceso de concatenación del String se convirtió en un dolor.

Lo estaba haciendo con el operador regular ‘+’.

Cuando cambié al ‘< <' fue como magia. Fue realmente rápido

Entonces, recordé mis viejos tiempos – algo así como 1998 – cuando estaba usando Java y concatenando String usando ‘+’ y cambiando de String a StringBuffer (y ahora nosotros, el desarrollador de Java tenemos el StringBuilder).

Creo que el proceso de + / < < en el mundo de Ruby es el mismo que + / StringBuilder.append en el mundo de Java.

La primera reasigna el objeto completo en la memoria y la otra simplemente señala una nueva dirección.

Concatenación que dices? ¿Qué #concat 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 

Con toda justicia, concat se alias como < < .

Aquí hay más formas de hacer esto:

 "String1" + "String2" "#{String1} #{String2}" String1<  

Y así ...