如何在Ruby中快速连接两个字符串

the*_*Man 4 ruby benchmarking ruby-on-rails

通常需要连接字符串,而在Ruby中我们有共同的方法:将一个连接,连接和插入到另一个中,或者使用concatString中的内置方法.(我们有多种方法可以实现灵活性,并简化从其他语言到Ruby的过渡.)

从...开始:

'a ' << 'z'      # => "a z"
'a '.concat('z') # => "a z"
'a ' + 'z'       # => "a z"
"a #{'z'}"       # => "a z"
Run Code Online (Sandbox Code Playgroud)

假设我们不想更改任何字符串并且不会将字符串分配给变量,加入它们的最快方法是什么,并且最快的方式是随着"左"字符串的大小增长而改变?

对于那些无法弄清楚我们为什么要发布这样的问题的人来说,这是为了帮助教育和展示完成特定任务的最有效方式.在这种情况下,一种语言的新手Ruby通常会拖延旧方法,并且无意中编写运行速度超过必要的代码.对编码风格进行简单更改可以使代码更快.

the*_*Man 5

从短字符串开始:

z = 'z'
'a ' << z      # => "a z"
'a '.concat(z) # => "a z"
'a ' + z       # => "a z"
"a #{z}"       # => "a z"

require 'fruity'
compare do
  append      { 'a ' << z}
  concat      { 'a '.concat(z)}
  plus        { 'a ' + z}
  interpolate { "a #{z}" }
end

# >> Running each test 65536 times. Test will take about 2 seconds.
# >> interpolate is similar to append
# >> append is similar to plus
# >> plus is faster than concat by 2x ± 0.1
Run Code Online (Sandbox Code Playgroud)

将"左"字符串增加到11个字符:

require 'fruity'
compare do
  append      { 'abcdefghij ' << z}
  concat      { 'abcdefghij '.concat(z)}
  plus        { 'abcdefghij ' + z}
  interpolate { "abcdefghij #{z}" }
end

# >> Running each test 65536 times. Test will take about 2 seconds.
# >> interpolate is similar to append
# >> append is similar to plus
# >> plus is faster than concat by 2x ± 1.0
Run Code Online (Sandbox Code Playgroud)

51个字符:

compare do
  append      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' << z}
  concat      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij '.concat(z)}
  plus        { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' + z}
  interpolate { "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij #{z}" }
end

# >> Running each test 32768 times. Test will take about 2 seconds.
# >> plus is faster than append by 2x ± 1.0
# >> append is similar to interpolate
# >> interpolate is similar to concat
Run Code Online (Sandbox Code Playgroud)

101:

compare do
  append      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' << z}
  concat      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij '.concat(z)}
  plus        { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' + z}
  interpolate { "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij #{z}" }
end

# >> Running each test 32768 times. Test will take about 2 seconds.
# >> plus is faster than interpolate by 2x ± 0.1
# >> interpolate is similar to append
# >> append is similar to concat
Run Code Online (Sandbox Code Playgroud)

501:

compare do
  append      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' << z}
  concat      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij '.concat(z)}
  plus        { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' + z}
  interpolate { "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij #{z}" }
end

# >> Running each test 16384 times. Test will take about 1 second.
# >> plus is faster than append by 2x ± 0.1
# >> append is similar to interpolate
# >> interpolate is similar to concat
Run Code Online (Sandbox Code Playgroud)

一旦字符串超过50个字符,其+表现始终优于其他字符.

在评论中提到了其中一些变异在左边的字符串.如果它是左侧的变量而不是文字字符串,那么会发生什么:

a = 'a'
z = 'z'

a << z # => "az"
a # => "az"

a = 'a'
a.concat(z) # => "az"
a # => "az"
Run Code Online (Sandbox Code Playgroud)

相比:

a + z # => "az"
a     # => "a"

"#{a} #{z}" # => "a z"
a           # => "a"
Run Code Online (Sandbox Code Playgroud)

注意:答案的初始版本使用了以下测试:

"a #{'z'}"
Run Code Online (Sandbox Code Playgroud)

问题是Ruby非常聪明,可以识别出'z'另一个文字并将字符串转换为:

"a z"
Run Code Online (Sandbox Code Playgroud)

最终结果是测试会比其他测试更加不公平.

  • 你的'interpolate`不是真正的插值,因为``z#{"z"}"`将被编译成`"z z"`.要在这里做真正的基准测试,你必须引入额外的变量`interpolate {z ='z'; "a#{z}"}` (2认同)