Вопрос

Я ищу более элегантный способ объединения строк в Ruby.

У меня есть следующая строка:

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

Есть ли более приятный способ сделать это?

И если уж на то пошло, в чем разница между << и +?

Это было полезно?

Решение

Вы можете сделать это несколькими способами:

  1. Как вы показали с << но это не тот обычный способ
  2. С интерполяцией строк

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

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

Второй метод кажется более эффективным с точки зрения памяти / скорости, судя по тому, что я видел (хотя и не измерял).Все три метода выдадут неинициализированную постоянную ошибку, когда ROOT_DIR равен нулю.

При работе с именами путей вы можете захотеть использовать File.join чтобы избежать путаницы с разделителем имен путей.

В конце концов, это дело вкуса.

Другие советы

В + operator - это обычный вариант конкатенации и, вероятно, самый быстрый способ объединения строк.

Разница между + и << это что << изменяет объект с его левой стороны, и + не делает.

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"

Если вы просто объединяете пути, вы можете использовать собственный метод File.join в Ruby.

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

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

Используя << ака concat гораздо эффективнее, чем +=, поскольку последний создает временный объект и переопределяет первый объект новым объектом.

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

выходной сигнал:

____________________________________________________________
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)

Поскольку это путь, я бы, вероятно, использовал array и join:

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

Вот еще один бенчмарк, вдохновленный эта суть.Он сравнивает конкатенацию (+), добавляя (<<) и интерполяция (#{}) для динамических и предопределенных строк.

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

выходной сигнал:

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)

Заключение:интерполяция при МРТ тяжелая.

Я бы предпочел использовать Pathname:

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

о нас << и + из ruby docs:

+:Возвращает новое Строка, содержащая other_str, объединенная с str

<<:Объединяет данный объект с str.Если объект имеет фиксированное число от 0 до 255, он преобразуется в символ перед конкатенацией.

итак, разница заключается в том, что становится первым операндом (<< вносит изменения на месте, + возвращает новую строку, поэтому она занимает больше памяти) и что будет, если первым операндом будет Fixnum (<< добавит, как если бы это был символ с кодом, равным этому числу, + вызовет ошибку)

Позвольте мне показать вам весь мой опыт в этом.

У меня был запрос, который вернул 32k записей, для каждой записи я вызвал метод для форматирования этой записи базы данных в форматированную строку, а затем объединил ее в строку, которая в конце всего этого процесса превратится в файл на диске.

Моя проблема заключалась в том, что к моменту записи, около 24 кб, процесс объединения строки стал болезненным.

Я делал это, используя обычный оператор '+'.

Когда я перешел на '<<" это было похоже на волшебство.Было действительно быстро.

Итак, я вспомнил свои старые времена - вроде 1998 года, - когда я использовал Java и объединял String с помощью '+' и сменил String на StringBuffer (и теперь у нас, разработчиков Java, есть StringBuilder).

Я считаю , что процесс + / << в Ruby world это то же самое, что и + / StringBuilder.append в мире Java.

Первый перераспределяет весь объект в памяти, а другой просто указывает на новый адрес.

Конкатенация, вы говорите?Как насчет #concat тогда метод?

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

По всей справедливости, concat имеет псевдоним как <<.

Вот еще несколько способов сделать это:

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

И так далее ...

Вы можете использовать + или << оператор, но в ruby .concat функция является наиболее предпочтительной, так как она намного быстрее, чем другие операторы.Вы можете использовать его следующим образом.

source = "#{ROOT_DIR}/".concat(project.concat.("/App.config"))

Вы также можете использовать % следующим образом:

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

Этот подход работает с ' также (одинарные) кавычки.

Ситуация имеет значение, например:

# 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

В первом примере объединение с + оператор не будет обновлять output возражать, однако, во втором примере, << оператор обновит output объект с каждой итерацией.Итак, для ситуации вышеуказанного типа, << это лучше.

Вы можете выполнить конкатенацию непосредственно в определении строки:

nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top