Ruby 中的字符串串联与插值

我刚刚开始学习 Ruby (第一次编程) ,有一个关于变量的基本语法问题,以及各种编写代码的方法。

克里斯 · 派恩的《学会编程》教会了我编写这样一个基本程序。

num_cars_again= 2
puts 'I own ' + num_cars_again.to_s + ' cars.'

这是很好的,但是后来我无意中发现了 ruby.learncodethehardway.com 的教程,并且被教导要像这样编写完全相同的程序... ..。

num_cars= 2
puts "I own #{num_cars} cars."

它们都输出相同的结果,但显然选项2是一个更短的方法。

有没有什么特别的原因让我选择一种格式而不是另一种?

34564 次浏览

每当 TIMTOWTDI (有一个以上的方法做到这一点) ,你应该寻找利弊。使用“字符串插值”(第二个)代替“字符串串联”(第一个) :

优点:

  • 少打字
  • 自动为您调用 to_s
  • 在 Ruby 社区中更加惯用
  • 在运行时更快地完成

缺点:

  • 自动为您调用 to_s(可能您认为您有一个字符串,而 to_s表示并不是您想要的,并且隐藏了它不是一个字符串的事实)
  • 要求您使用 "而不是 '来划分字符串(也许您有使用 '的习惯,或者您以前使用 '键入了一个字符串,只是后来需要使用字符串插值)

@ user1181898-恕我直言,这是因为更容易看到发生了什么。根据@Phrogz 的观点,字符串插值会自动为您调用 To _ s。作为一个初学者,你需要看看“引擎盖下”发生了什么,这样你才能学习这个概念,而不是死记硬背。

就像学数学一样。你学习“长”的方法是为了理解这些概念,这样一旦你真正知道自己在做什么,你就可以走捷径了。根据我的经验,我在 Ruby 方面还没有那么先进,但是我已经犯了足够多的错误来建议人们不要做什么。希望这个能帮上忙。

插值和连接都有自己的优点和缺点。下面我给出了一个基准,它清楚地说明了在哪里使用连接和在哪里使用插值。

require 'benchmark'


iterations = 1_00_000
firstname = 'soundarapandian'
middlename = 'rathinasamy'
lastname = 'arumugam'


puts 'With dynamic new strings'
puts '===================================================='
5.times do
Benchmark.bm(10) do |benchmark|
benchmark.report('concatination') do
iterations.times do
'Mr. ' + firstname + middlename + lastname + ' aka soundar'
end
end


benchmark.report('interpolaton') do
iterations.times do
"Mr. #{firstname} #{middlename} #{lastname} aka soundar"
end
end
end
puts '--------------------------------------------------'
end


puts 'With predefined strings'
puts '===================================================='
5.times do
Benchmark.bm(10) do |benchmark|
benchmark.report('concatination') do
iterations.times do
firstname + middlename + lastname
end
end


benchmark.report('interpolaton') do
iterations.times do
"#{firstname} #{middlename} #{lastname}"
end
end
end
puts '--------------------------------------------------'
end

下面是基准测试的结果

Without predefined strings
====================================================
user     system      total        real
concatination  0.170000   0.000000   0.170000 (  0.165821)
interpolaton  0.130000   0.010000   0.140000 (  0.133665)
--------------------------------------------------
user     system      total        real
concatination  0.180000   0.000000   0.180000 (  0.180410)
interpolaton  0.120000   0.000000   0.120000 (  0.125051)
--------------------------------------------------
user     system      total        real
concatination  0.140000   0.000000   0.140000 (  0.134256)
interpolaton  0.110000   0.000000   0.110000 (  0.111427)
--------------------------------------------------
user     system      total        real
concatination  0.130000   0.000000   0.130000 (  0.132047)
interpolaton  0.120000   0.000000   0.120000 (  0.120443)
--------------------------------------------------
user     system      total        real
concatination  0.170000   0.000000   0.170000 (  0.170394)
interpolaton  0.150000   0.000000   0.150000 (  0.149601)
--------------------------------------------------
With predefined strings
====================================================
user     system      total        real
concatination  0.070000   0.000000   0.070000 (  0.067735)
interpolaton  0.100000   0.000000   0.100000 (  0.099335)
--------------------------------------------------
user     system      total        real
concatination  0.060000   0.000000   0.060000 (  0.061955)
interpolaton  0.130000   0.000000   0.130000 (  0.127011)
--------------------------------------------------
user     system      total        real
concatination  0.090000   0.000000   0.090000 (  0.092136)
interpolaton  0.110000   0.000000   0.110000 (  0.110224)
--------------------------------------------------
user     system      total        real
concatination  0.080000   0.000000   0.080000 (  0.077587)
interpolaton  0.110000   0.000000   0.110000 (  0.112975)
--------------------------------------------------
user     system      total        real
concatination  0.090000   0.000000   0.090000 (  0.088154)
interpolaton  0.140000   0.000000   0.140000 (  0.135349)
--------------------------------------------------

结论

如果字符串已经定义并且确定它们永远不会为零,则使用连接,否则使用插值。使用适当的一个,这将导致更好的性能比一个很容易缩进。

如果使用字符串作为缓冲区,我发现使用串联(String#concat)会更快。

require 'benchmark/ips'


puts "Ruby #{RUBY_VERSION} at #{Time.now}"
puts


firstname = 'soundarapandian'
middlename = 'rathinasamy'
lastname = 'arumugam'


Benchmark.ips do |x|
x.report("String\#<<") do |i|
buffer = String.new


while (i -= 1) > 0
buffer << 'Mr. ' << firstname << middlename << lastname << ' aka soundar'
end
end


x.report("String interpolate") do |i|
buffer = String.new


while (i -= 1) > 0
buffer << "Mr. #{firstname} #{middlename} #{lastname} aka soundar"
end
end


x.compare!
end

结果:

Ruby 2.3.1 at 2016-11-15 15:03:57 +1300


Warming up --------------------------------------
String#<<   230.615k i/100ms
String interpolate   234.274k i/100ms
Calculating -------------------------------------
String#<<      2.345M (± 7.2%) i/s -     11.761M in   5.041164s
String interpolate      1.242M (± 5.4%) i/s -      6.325M in   5.108324s


Comparison:
String#<<:  2344530.4 i/s
String interpolate:  1241784.9 i/s - 1.89x  slower

我猜测,插值会产生一个临时的字符串,这就是为什么它比较慢的原因。

这里是一个完整的基准,也比较 Kernel#formatString#+,因为它的所有方法构造动态字符串在红宝石,我知道

require 'benchmark/ips'


firstname = 'soundarapandian'
middlename = 'rathinasamy'
lastname = 'arumugam'


FORMAT_STR = 'Mr. %<firstname>s %<middlename>s %<lastname>s aka soundar'
Benchmark.ips do |x|
x.report("String\#<<") do |i|
str = String.new
str << 'Mr. ' << firstname << ' ' << middlename << ' ' << lastname << ' aka soundar'
end


x.report "String\#+" do
'Mr. ' + firstname + ' ' + middlename + ' ' + lastname + ' aka soundar'
end


x.report "format" do
format(FORMAT_STR, firstname: firstname, middlename: middlename, lastname: lastname)
end


x.report("String interpolate") do |i|
"Mr. #{firstname} #{middlename} #{lastname} aka soundar"
end


x.compare!
end

Ruby 2.6.5的结果

Warming up --------------------------------------
String#<<
94.597k i/100ms
String#+    75.512k i/100ms
format    73.269k i/100ms
String interpolate   164.005k i/100ms
Calculating -------------------------------------
String#<<     91.385B (±16.9%) i/s -    315.981B
String#+    905.389k (± 4.2%) i/s -      4.531M in   5.013725s
format    865.746k (± 4.5%) i/s -      4.323M in   5.004103s
String interpolate    161.694B (±11.3%) i/s -    503.542B


Comparison:
String interpolate: 161693621120.0 i/s
String#<<: 91385051886.2 i/s - 1.77x  slower
String#+:   905388.7 i/s - 178590.27x  slower
format:   865745.8 i/s - 186768.00x  slower