Ruby'de dizeleri birleştirmenin daha zarif bir yolunu arıyorum.
Aşağıdaki satır var:
source = "#{ROOT_DIR}/" << project << "/App.config"
Bunu yapmanın daha hoş bir yolu var mı?
Ve bu konuda <<
ve arasındaki fark +
nedir?
Ruby'de dizeleri birleştirmenin daha zarif bir yolunu arıyorum.
Aşağıdaki satır var:
source = "#{ROOT_DIR}/" << project << "/App.config"
Bunu yapmanın daha hoş bir yolu var mı?
Ve bu konuda <<
ve arasındaki fark +
nedir?
Yanıtlar:
Bunu çeşitli şekillerde yapabilirsiniz:
<<
ama bu değil olağan yoluDize enterpolasyonu ile
source = "#{ROOT_DIR}/#{project}/App.config"
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.join
adı ayırıcısını karıştırmamak için kullanmak isteyebilirsiniz .
Sonunda, bu bir zevk meselesi.
+
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"
+
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
5.times do ... end
her 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.
Sadece yolları birleştiriyorsanız Ruby'nin kendi File.join yöntemini kullanabilirsiniz.
source = File.join(ROOT_DIR, project, 'App.config')
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)
Bu bir yol olduğundan muhtemelen dizi kullanmak ve katılmak:
source = [ROOT_DIR, project, 'App.config'] * '/'
İş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.
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)
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.
(Pathname(ROOT_DIR) + project + 'App.config').to_s
bir dize nesnesi döndürmek istiyorsanız çağırmanız gerekir .
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.
Birleştirme mi diyorsun? Peki ya #concat
o 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 concat
ad olarak adlandırılır <<
.
"foo" "bar" 'baz" #=> "foobarabaz"
İşte bunu yapmanın diğer yolları:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
Ve bunun gibi ...
Kullanabilir +
veya <<
operatör kullanabilirsiniz , ancak yakut .concat
iş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"))
.
son hayırından sonra fazladan var concat
mı?
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ç, output
nesneyi her yinelemeyle güncelleştirir . Yani, yukarıdaki durum türü <<
için daha iyidir.
Özel durumunuz için, Array#join
dosya 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"