Ruby中的字符串串联与插值


74

我刚刚开始学习Ruby(首次编程),并且对变量和各种编写代码的方式有一个基本的语法问题。

克里斯·派恩(Chris Pine)的“学习编程”教会了我编写像这样的基本程序。

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的方法要短得多。

我为什么要使用一种格式而不是另一种格式?


5
啊。我讨厌初学者的书多久教给您一种不自然的做事方法,而至少没有告诉您存在其他选择。+1尚未提出的合法问题。
Andrew Grimm'4

Answers:


72

只要有TIMTOWTDI(有多种方法可以做到),就应该寻找优点和缺点。使用“字符串插值”(第二个)代替“字符串串联”(第一个):

优点:

  • 打字少
  • 自动to_s为您打电话
  • Ruby社区中的更多惯用语
  • 在运行时更快地完成

缺点:

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

24
不要忘记“更快”的方面。在此示例中,字符串连接必须总共创建3个字符串,而字符串插值仅创建一个。
Dominik Honnef'4

4
如果您关心基准测试,它也会更快:请尝试REPL
mrlee

2
非常感谢您的回答。快速提问。克里斯·派恩(Chris Pine)的书为什么要教更长的方法呢?也许对于初学者来说学习更长的时间更好?他的书大部分时间都说得更好,所以我想知道是否是出于某种原因(因为我只是在学习),所以我应该继续按自己的方式做,还是以更好的方式前进。有任何想法吗?
Jeff H.

6
我的猜测是:对于新程序员来说,“使用to_s知名运算符将字符串连接在一起”是一个比“使用自定义语法评估任意代码,调用结果并将其注入字符串中间更简单的概念。学习任何新事物时,“易于理解的方式”与“专业人员的方式”通常会有所不同。
Phrogz

7
我知道我对这次讨论太迟了,但是我这样做的主要原因有两个。首先,出于Phrogz给出的原因:我试图使用他们已经知道的概念使其尽可能简单。在第一版中,我什至没有涵盖双引号字符串!学习编程时,人们想要的最后一件事是创建字符串的六种不同语法。其次,由于隐式to_s。是的,对于我们这些了解类型和转换的人来说,这很方便。但是对于新程序员来说,了解这些概念确实很重要。
克里斯·派恩

9

内插法和隐含法都有其优点和缺点。在下面,我给出了一个基准,清楚地说明了在哪里使用混叠和在哪里使用插值。

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)
--------------------------------------------------

结论

如果已经定义了字符串并且确定它们永远不会为零,则使用隐含法,否则使用插值法。使用合适的字符串会比容易缩进的字符串产生更好的性能。


您使用了哪个Ruby版本?
La-comadreja 2014年

1
我在ruby 2.5.0中尝试过,并且interpolationconcatenation这两种情况都快。由于评论的长度限制,我无法在此处粘贴结果,但是您可以自己尝试。
Peter T.

我不确定这是否完全公平,"#{firstname} #{middlename} #{lastname}"应该将其与而firstname + " " + middlename + " " + lastname不是进行比较 firstname + middlename + lastname(5串与3串
并列

请注意,当我更改基准以使其具有可比性时(通过删除案例中的内部空格"#{firstname} #{middlename} #{lastname}"或在concatination案例中添加空格),插值总是明显更快(至少在Mac OSX上使用Ruby 2.6.3)。
米卡洛特'20

4

@ user1181898-恕我直言,这是因为更容易看到正在发生的事情。就@Phrogz而言,字符串插值会自动为您调用to_s。作为初学者,您需要了解“引擎盖下”正在发生的事情,以便您学习该概念,而不仅仅是死记硬背。

认为它就像学习数学一样。您将学习“漫长的”方法,以便理解这些概念,以便在真正知道自己在做什么之后就可以采用快捷方式。我从经验b / c的角度讲,我在Ruby方面还没有那么先进,但是我犯了很多错误,可以向人们建议不要做什么。希望这可以帮助。


2

如果您使用字符串作为缓冲区,我发现使用串联(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

猜测一下,我会说插值会生成一个临时字符串,这就是为什么它慢一些的原因。


0

这是一个完整的基准,它也可以进行比较Kernel#formatString#+因为我知道这是在ruby中构造动态字符串的所有方法,🤔

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.