Ruby中的string连接
我正在寻找一种在Ruby中连接string的更优雅的方式。
我有以下行:
source = "#{ROOT_DIR}/" << project << "/App.config"
有没有更好的方法呢?
就此而言, <<
和+
什么区别呢?
你可以用几种方法来做到这一点:
- 正如你所表明的那样,
<<
但这不是通常的方式 -
用string插值
source = "#{ROOT_DIR}/#{project}/App.config"
-
与
+
source = "#{ROOT_DIR}/" + project + "/App.config"
第二种方法似乎从我所见过的内存/速度方面来看效率更高(虽然没有进行测量)。 当ROOT_DIR为零时,所有这三种方法都会引发一个未初始化的常量错误。
在处理path名时,可能需要使用File.join
来避免弄乱path名分隔符。
最后,这是一个品味的问题。
+
运算符是正常的级联select,可能是连接string的最快方法。
+
和<<
之间的区别在于<<
左侧改变对象, +
不改变。
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"
如果你只是连接path,你可以使用Ruby自己的File.join方法。
source = File.join(ROOT_DIR, project, 'App.config')
来自http://greyblake.com/blog/2012/09/02/ruby-perfomance-tricks/
使用<<
aka concat
比+=
效率更高,因为后者创build了一个时间对象,并用新对象覆盖了第一个对象。
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)
由于这是一个path,我可能会使用数组和连接:
source = [ROOT_DIR, project, 'App.config'] * '/'
我更喜欢使用path名:
require 'pathname' # pathname is in stdlib Pathname(ROOT_DIR) + project + 'App.config'
关于<<
和+
从ruby文档:
+
:返回一个包含连接到str的other_str的新string
<<
:将给定对象连接到str。 如果对象是0到255之间的一个Fixnum,它将在连接之前转换为一个字符。
所以区别在于第一个操作数是什么( <<
改变了位置, +
返回新的string,所以它是内存较重),如果第一个操作数是Fixnum( <<
将添加,就好像它是代码等于那个数字, +
会引起错误)
让我告诉你我所有的经验。
我有一个查询返回32K的logging,每个logging我调用一个方法来格式化该数据库logging成为一个格式化的string,而不是连接成一个string,在这个过程的最后将变成一个文件在磁盘。
我的问题是,通过logging,大约24K,连接string的过程开始痛苦。
我正在使用常规的“+”运算符。
当我改成“<<”就像魔术一样。 真的很快
所以,我记得我以前的时光 – 从1998年开始 – 当时我正在使用Java,并使用“+”连接string,并将string更改为StringBuffer(现在我们Java开发人员拥有StringBuilder)。
我相信Ruby世界中的+ / <<的过程与Java世界中的+ / StringBuilder.append相同。
第一个重新分配整个对象在内存中,另一个只是指向一个新的地址。
你说的连接? 那么#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
被别名为<<
。
这是由这个要点启发的另一个基准。 它比较dynamic和预定义string的串联( +
),附加( <<
)和插值( #{}
)。
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)
结论:MRI中的插值很重。
这里有更多的方法来做到这一点:
"String1" + "String2" "#{String1} #{String2}" String1<<String2
等等 …