Ruby'de dize birleştirme


Yanıtlar:


574

Bunu çeşitli şekillerde yapabilirsiniz:

  1. Birlikte görüldüğü gibi <<ama bu değil olağan yolu
  2. Dize enterpolasyonu ile

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

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

İkinci yöntem, gördüğüm kadarıyla bellek / hız açısından daha verimli görünüyor (yine de ölçülmedi). Her üç yöntem de ROOT_DIR sıfır olduğunda başlatılmamış bir sabit hata atar.

Yol adları ile uğraşırken, yol File.joinadı ayırıcısını karıştırmamak için kullanmak isteyebilirsiniz .

Sonunda, bu bir zevk meselesi.


7
Ruby ile pek tecrübeli değilim. Ancak genellikle çok sayıda dizeyi birleştirdiğiniz durumlarda, dizeleri bir diziye ekleyerek performans elde edebilirsiniz ve sonunda dizeyi atomik olarak bir araya getirebilirsiniz. O zaman << faydalı olabilir mi?
PEZ

1
Yine de uzun dizeyi bir kopyasını belleğe eklemeniz gerekecek. << tek bir karakterle << yapabilmeniz dışında aşağı yukarı + ile aynıdır.
Keltia

9
Dizinin öğelerinde << kullanmak yerine Array # join kullanın, çok daha hızlıdır.
Grant Hutchins

94

+Operatör, normal birleştirme seçimdir ve muhtemelen concatenate dizeleri için en hızlı yoldur.

Arasındaki fark +ve <<olmasıdır <<sol taraftaki nesne değiştirir ve +değildir.

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
+ İşleci, dizeleri birleştirmenin kesinlikle en hızlı yolu değildir. Her kullandığınızda, bir kopya oluşturur, ancak << yerinde bir araya gelir ve çok daha performanslıdır.
Evil Alabalık

5
Çoğu kullanım için enterpolasyon +ve <<yaklaşık aynı olacaktır. Çok sayıda dizeyle veya gerçekten büyük dizelerle uğraşıyorsanız, bir fark görebilirsiniz. Ne kadar benzer performans gösterdikleri beni şaşırttı. gist.github.com/2895311
Matt Burke

8
Jruby sonuçlarınız erken çalıştırma JVM aşırı yüklenmesi ile enterpolasyona çarpıktır. Test paketini birkaç kez çalıştırırsanız (aynı işlemde - 5.times do ... endher yorumlayıcı için her şeyi bir blok halinde sarın ), daha doğru sonuçlar elde edersiniz. Testlerim enterpolasyonun tüm Ruby tercümanlarında en hızlı yöntem olduğunu gösterdi. Ben umuyordum <<hızlı olması, ama bu yüzden biz kriter var.
12'de womble

Ruby hakkında çok bilgili olmama, mutasyonun yığın veya yığın üzerinde yapılıp yapılmadığını merak ediyorum? Öbek üzerinde, daha hızlı olması gerektiği gibi görünen bir mutasyon işlemi bile muhtemelen bir çeşit malloc içerir. Onsuz, bir arabellek taşması beklenir. Yığın kullanımı oldukça hızlı olabilir, ancak ortaya çıkan değer muhtemelen bir yığın işlemi gerektirecek şekilde yığın üzerine yerleştirilir. Sonunda, değişken referansı yerinde bir mutasyon gibi görünse bile bellek işaretçisinin yeni bir adres olmasını bekliyorum. Peki, gerçekten, bir fark var mı?
Robin Coe

79

Sadece yolları birleştiriyorsanız Ruby'nin kendi File.join yöntemini kullanabilirsiniz.

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

5
Bu o zamandan beri yolun yolu gibi görünüyor, o zaman yakut farklı yol ayırıcıları olan sistemde doğru dizeyi oluşturmaya özen gösterecektir.
PEZ

26

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

<<Aka kullanmak , ikincisi geçici bir nesne oluşturduğundan ve yeni nesneyle ilk nesneyi geçersiz kıldığından concatçok daha etkilidir +=.

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

çıktı:

____________________________________________________________
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

Bu bir yol olduğundan muhtemelen dizi kullanmak ve katılmak:

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

9

İşte bu özden esinlenen başka bir kriter . Dinamik ve önceden tanımlanmış dizeler için concatenation ( +), appending ( <<) ve interpolation ( #{}) yöntemlerini karşılaştırır.

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

çıktı:

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)

Sonuç: MRG'de enterpolasyon ağırdır.


Dizeler şimdi değişmez olmaya başladığı için, bunun için yeni bir kriter görmek isterim.
18'de bibstha

7

Pathname kullanmayı tercih ederim:

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

yakut dokümanlar hakkında <<ve +onlardan:

+: Str ile bitiştirilmiş other_str içeren yeni bir String döndürür

<<: Verilen nesneyi str. Nesne 0 ile 255 arasında bir Fixnum ise, birleştirme işleminden önce bir karaktere dönüştürülür.

Fark Birinci işlenen hale gelir ne yani ( <<yerinde değişiklikler yapar +(ilk işlenen Fixnum ise olacak ve ne döner yeni dize bellek ağır yani) <<o kodla karakter o numaraya eşit sanki katacak, +yükseltecek hata)


2
Sadece arg bir mutlak yol ise, alıcı yolu yok sayılır, çünkü bir Pathname üzerinde '+' çağıran tehlikeli olduğunu keşfetti: Pathname('/home/foo') + '/etc/passwd' # => #<Pathname:/etc/passwd>. Bu, rubydoc örneğine dayanan tasarım gereğidir. File.join dosyasının daha güvenli olduğu anlaşılıyor.
Kelvin

Ayrıca (Pathname(ROOT_DIR) + project + 'App.config').to_sbir dize nesnesi döndürmek istiyorsanız çağırmanız gerekir .
lacostenycoder

6

Size bu konudaki tüm deneyimlerimi göstereyim.

32k kayıt döndüren bir sorgu vardı, her kayıt için bu veritabanı kaydını biçimlendirilmiş bir dize biçimlendirmek için bir yöntem çağırdı ve daha sonra tüm bu işlemin sonunda diskte bir dosyaya dönüşecek bir String içine bitiştirmek.

Benim sorunum, rekor 24 k civarında, String bitirme sürecinin bir acı döndü gidiyordu.

Bunu normal '+' operatörünü kullanarak yapıyordum.

'<<' olarak değiştirdiğimde sihir gibiydi. Gerçekten hızlıydı.

Yani, benim eski zamanları hatırladım - bir tür 1998 - Java kullanırken ve '+' kullanarak String bitirme ve String'den StringBuffer (ve şimdi, Java geliştirici StringBuilder var) değiştirdi.

Ruby dünyasında + / << işleminin Java dünyasında + / StringBuilder.append ile aynı olduğuna inanıyorum.

Birincisi bellekteki tüm nesneyi yeniden tahsis eder, diğeri sadece yeni bir adrese işaret eder.


5

Birleştirme mi diyorsun? Peki ya #concato zaman yöntem?

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

Tüm adalet içinde, diğer concatad olarak adlandırılır <<.


7
Dizeleri başkaları tarafından belirtilmeyen bir şekilde birleştirmenin bir yolu daha var ve bu sadece yan yana geliyor:"foo" "bar" 'baz" #=> "foobarabaz"
Boris Stitnicky

Diğerlerine not: Bunun tek bir alıntı olması değil, diğerleri gibi bir çift alıntı olması gerekiyordu. Düzgün bir yöntem!
Joshua Pinter

5

İşte bunu yapmanın diğer yolları:

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

Ve bunun gibi ...


2

Aşağıdaki gibi de kullanabilirsiniz %:

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

Bu yaklaşım '(tek) tırnak işareti ile de çalışır.


2

Kullanabilir +veya <<operatör kullanabilirsiniz , ancak yakut .concatişlevinde diğer operatörlerden çok daha hızlı olduğu için en çok tercih edilen işlevdir. Gibi kullanabilirsiniz.

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

Sanırım .son hayırından sonra fazladan var concatmı?
lacostenycoder

1

Durum önemlidir, örneğin:

# 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

İlk örnekte, işleçle birleştirme nesneyi +güncelleştirmez output, ancak ikinci örnekte <<işleç, outputnesneyi her yinelemeyle güncelleştirir . Yani, yukarıdaki durum türü <<için daha iyidir.


1

Doğrudan dize tanımında birleştirebilirsiniz:

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

0

Özel durumunuz için, Array#joindosya yolu türü dize oluştururken de kullanabilirsiniz :

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

Bunun, farklı türleri otomatik olarak dizeye dönüştürmenin hoş bir yan etkisi vardır:

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

0

Kukla için:

$username = 'lala'
notify { "Hello ${username.capitalize}":
    withpath => false,
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.