Rangkaian string di Ruby


364

Saya mencari cara yang lebih elegan untuk menggabungkan string di Ruby.

Saya memiliki baris berikut:

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

Apakah ada cara yang lebih baik untuk melakukan ini?

Dan untuk apa perbedaan antara <<dan +?


3
Pertanyaan ini stackoverflow.com/questions/4684446/… sangat terkait.
Eye

<< ini adalah cara yang lebih efisien untuk melakukan penggabungan.
Taimoor Changaiz

Jawaban:


575

Anda dapat melakukannya dengan beberapa cara:

  1. Seperti yang Anda tunjukkan dengan <<tetapi itu bukan cara yang biasa
  2. Dengan interpolasi string

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

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

Metode kedua tampaknya lebih efisien dalam hal memori / kecepatan dari apa yang saya lihat (meskipun tidak diukur). Ketiga metode ini akan menampilkan kesalahan konstan yang tidak diinisialisasi ketika ROOT_DIR nihil.

Saat berurusan dengan nama path, Anda mungkin ingin menggunakan File.joinuntuk menghindari mengacaukan pemisah pathname.

Pada akhirnya, ini adalah masalah selera.


7
Saya tidak terlalu berpengalaman dengan ruby. Tetapi umumnya dalam kasus di mana Anda menggabungkan banyak string, Anda sering dapat memperoleh kinerja dengan menambahkan string ke array dan kemudian pada akhirnya menempatkan string bersama secara atom. Lalu << bisa bermanfaat?
PEZ

1
Anda harus menambahkan memori dan menyalin string yang lebih panjang ke dalamnya. << kurang lebih sama dengan + kecuali bahwa Anda dapat << dengan satu karakter.
Keltia

9
Alih-alih menggunakan << pada elemen array, gunakan Array # join, ini jauh lebih cepat.
Grant Hutchins

94

The +operator adalah pilihan Rangkaian normal, dan mungkin adalah cara tercepat untuk string concatenate.

Perbedaan antara +dan <<adalah yang <<mengubah objek di sisi kiri, dan +tidak.

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"

32
Operator + jelas bukan cara tercepat untuk menggabungkan string. Setiap kali Anda menggunakannya, itu membuat salinan, sedangkan << digabungkan di tempat dan jauh lebih performant.
Trout Jahat

5
Untuk sebagian besar kegunaan, interpolasi, +dan <<akan hampir sama. Jika Anda berurusan dengan banyak string, atau string yang sangat besar, maka Anda mungkin melihat perbedaannya. Saya terkejut dengan betapa miripnya mereka tampil. gist.github.com/2895311
Matt Burke

8
Hasil jruby Anda condong terhadap interpolasi oleh JVM yang dijalankan sebelumnya. Jika Anda menjalankan test suite beberapa kali (dalam proses yang sama - jadi bungkuslah semuanya dalam satu 5.times do ... endblok) untuk setiap penerjemah, Anda akan mendapatkan hasil yang lebih akurat. Pengujian saya menunjukkan interpolasi adalah metode tercepat, di semua penafsir Ruby. Saya berharap <<menjadi yang tercepat, tapi itu sebabnya kami melakukan benchmark.
womble

Tidak terlalu berpengalaman tentang Ruby, saya ingin tahu apakah mutasi dilakukan pada stack atau heap? Jika di tumpukan, bahkan operasi mutasi, yang sepertinya harus lebih cepat, mungkin melibatkan beberapa bentuk malloc. Tanpa itu, saya harapkan buffer overflow. Menggunakan stack bisa sangat cepat tetapi nilai yang dihasilkan mungkin ditempatkan di heap, membutuhkan operasi malloc. Pada akhirnya, saya berharap penunjuk memori menjadi alamat baru, bahkan jika referensi variabel membuatnya seperti mutasi di tempat. Jadi, sungguh, apakah ada perbedaan?
Robin Coe

79

Jika Anda baru saja menyatukan jalur, Anda dapat menggunakan metode File.join milik Ruby sendiri.

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

5
Ini tampaknya menjadi cara untuk pergi sejak itu ruby ​​akan mengurus pembuatan string yang benar pada sistem dengan pemisah jalur yang berbeda.
PEZ

26

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

Menggunakan <<alias concatjauh lebih efisien daripada +=, karena yang terakhir menciptakan objek temporal dan menimpa objek pertama dengan objek baru.

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

keluaran:

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

11

Karena ini adalah jalur, saya mungkin akan menggunakan array dan bergabung:

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

9

Berikut patokan lain yang terinspirasi oleh intisari ini . Ini membandingkan concatenation ( +), appending ( <<) dan interpolasi ( #{}) untuk string dinamis dan yang telah ditentukan.

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

keluaran:

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)

Kesimpulan: interpolasi dalam MRI berat.


Karena string mulai tidak berubah sekarang, saya ingin melihat patokan baru untuk ini.
bibstha

7

Saya lebih suka menggunakan Pathname:

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

tentang <<dan +dari ruby ​​docs:

+: Mengembalikan String baru yang berisi other_str yang digabungkan ke str

<<: Menggabungkan objek yang diberikan ke str. Jika objek adalah Fixnum antara 0 dan 255, itu dikonversi ke karakter sebelum penggabungan.

jadi perbedaannya adalah apa yang menjadi operan pertama ( <<membuat perubahan di tempat, +mengembalikan string baru sehingga memori lebih berat) dan apa yang akan terjadi jika operan pertama adalah Fixnum ( <<akan menambahkan seolah-olah itu karakter dengan kode yang sama dengan angka itu, +akan menaikkan kesalahan)


2
Saya baru saja menemukan bahwa memanggil '+' pada Pathname bisa berbahaya karena jika arg adalah path absolut, jalur penerima diabaikan: Pathname('/home/foo') + '/etc/passwd' # => #<Pathname:/etc/passwd>. Ini dengan desain, berdasarkan contoh rubydoc. Tampaknya File.join lebih aman.
Kelvin

Anda juga perlu menelepon (Pathname(ROOT_DIR) + project + 'App.config').to_sjika ingin mengembalikan objek string.
lacostenycoder

6

Biarkan saya menunjukkan kepada Anda semua pengalaman saya dengan itu.

Saya memiliki permintaan yang mengembalikan 32 ribu catatan, untuk setiap catatan saya memanggil metode untuk memformat catatan basis data itu menjadi string yang diformat dan kemudian menggabungkannya menjadi String yang pada akhir semua proses ini akan berubah menjadi file dalam disk.

Masalah saya adalah bahwa dengan catatan berjalan, sekitar 24rb, proses menyatukan String menjadi sangat menyakitkan.

Saya melakukan itu menggunakan operator '+' biasa.

Ketika saya berubah ke '<<' itu seperti sihir. Sangat cepat.

Jadi, saya ingat masa lalu saya - semacam 1998 - ketika saya menggunakan Java dan menyatukan String menggunakan '+' dan berubah dari String ke StringBuffer (dan sekarang kami, pengembang Java memiliki StringBuilder).

Saya percaya bahwa proses + / << di dunia Ruby sama dengan + / StringBuilder.append di dunia Java.

Yang pertama realokasi seluruh objek dalam memori dan yang lainnya hanya menunjuk ke alamat baru.


5

Katanya konkatasi? Bagaimana dengan #concatmetode itu?

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

Dalam semua keadilan, concatalias <<.


7
Ada satu lagi cara merekatkan tali bersama-sama yang tidak disebutkan oleh orang lain, dan itu adalah dengan penjajaran belaka:"foo" "bar" 'baz" #=> "foobarabaz"
Boris Stitnicky

Catatan untuk orang lain: Itu tidak seharusnya menjadi kutipan tunggal, tetapi kutipan ganda seperti yang lainnya. Metode rapi!
Joshua Pinter

5

Berikut ini lebih banyak cara untuk melakukan ini:

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

Dan seterusnya ...


2

Anda juga dapat menggunakan %sebagai berikut:

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

Pendekatan ini bekerja dengan 'tanda kutip (tunggal) juga.


2

Anda dapat menggunakan +atau <<operator, tetapi dalam .concatfungsi ruby adalah yang paling disukai, karena jauh lebih cepat daripada operator lain. Anda bisa menggunakannya seperti.

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

Saya pikir Anda memiliki tambahan .setelah concatno terakhir Anda ?
lacostenycoder

1

Situasi penting, misalnya:

# 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

Dalam contoh pertama, menggabungkan dengan +operator tidak akan memperbarui outputobjek, namun, dalam contoh kedua, <<operator akan memperbarui outputobjek dengan setiap iterasi. Jadi, untuk jenis situasi di atas, <<lebih baik.


1

Anda dapat menggabungkan dalam definisi string secara langsung:

nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"

0

Untuk kasus khusus Anda, Anda juga dapat menggunakan Array#joinketika membangun tipe jalur file string:

string = [ROOT_DIR, project, 'App.config'].join('/')]

Ini memiliki efek samping yang menyenangkan dengan secara otomatis mengonversi berbagai jenis menjadi string:

['foo', :bar, 1].join('/')
=>"foo/bar/1"

0

Untuk boneka:

$username = 'lala'
notify { "Hello ${username.capitalize}":
    withpath => false,
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.