在Ruby中构建字符串时,为什么铲运算符(<<)比plus-equals(+ =)更受欢迎?

eri*_*own 149 ruby string optimization

我正在研究Ruby Koans.

about_strings.rb中test_the_shovel_operator_modifies_the_original_stringKoan 包含以下注释:

在构建字符串时,Ruby程序员倾向于使用铲运算符(<<)而不是正等运算符(+ =).为什么?

我的猜测是它涉及速度,但我不明白引擎盖下的动作会导致铲子操作员更快.

有人能够解释这个偏好背后的细节吗?

noo*_*odl 253

证明:

a = 'foo'
a.object_id #=> 2154889340
a << 'bar'
a.object_id #=> 2154889340
a += 'quux'
a.object_id #=> 2154742560
Run Code Online (Sandbox Code Playgroud)

因此<<改变原始字符串而不是创建新字符串.其原因在于ruby a += b是一种语法简写a = a + b(对于其他<op>=运算符也是如此),这是一个赋值.另一方面<<,别名concat()可以就地改变接收器.

  • 上面的@CincinnatiJoe链接似乎被破坏了,这是一个新的链接:[关于字符串的更多信息](http://library.edgecase.com/a-little-more-about-strings) (8认同)
  • 其中一位EdgeCase人员发布了一个关于性能数字的解释:[关于字符串的更多信息](http://library.edgecase.com/Ruby/2010/10/31/a-little-more-about-strings.html ) (5认同)
  • 谢谢,noodl!所以,实质上,<<更快,因为它不会创建新对象? (3认同)

Nem*_*157 80

绩效证明:

#!/usr/bin/env ruby

require 'benchmark'

Benchmark.bmbm do |x|
  x.report('+= :') do
    s = ""
    10000.times { s += "something " }
  end
  x.report('<< :') do
    s = ""
    10000.times { s << "something " }
  end
end

# Rehearsal ----------------------------------------
# += :   0.450000   0.010000   0.460000 (  0.465936)
# << :   0.010000   0.000000   0.010000 (  0.009451)
# ------------------------------- total: 0.470000sec
# 
#            user     system      total        real
# += :   0.270000   0.010000   0.280000 (  0.277945)
# << :   0.000000   0.000000   0.000000 (  0.003043)
Run Code Online (Sandbox Code Playgroud)


Kib*_*gon 70

一个正在学习Ruby作为他的第一个编程语言的朋友在Ruby Koans系列中通过Ruby中的Strings时问了我同样的问题.我用下面的比喻向他解释了;

你有一杯半满的水,你需要重新装满你的杯子.

第一种方法是采用新玻璃杯,用水龙头将水填充到中途,然后用第二个半满的玻璃杯重新装满水杯.每次需要重新装满玻璃杯时都会这样做.

第二种方式是你拿半满的玻璃杯直接用水龙头加水.

在一天结束时,如果您每次需要重新装满玻璃杯时选择更换新玻璃杯,您可以使用更多眼镜进行清洁.

这同样适用于铲运营商和正等运营商.加上相同的操作员每次需要重新填充玻璃时都会选择一个新的"玻璃",而铲子操作员只需要使用相同的玻璃并重新填充它.在一天结束时,Plus等于运营商的更多"玻璃"收集.

  • 很好的类比但可怕的结论.你必须补充说,眼镜是由其他人清洗的,所以你不必关心它们. (5认同)
  • 很好的比喻,我认为得出了一个很好的结论。我认为问题不在于谁必须清洁玻璃,而在于所使用的玻璃杯的数量。您可以想象某些应用程序正在突破其机器上的内存限制,并且这些机器一次只能清洁一定数量的玻璃杯。 (4认同)
  • 很好比喻,喜欢它. (2认同)

Ton*_*ony 11

这是一个老问题,但我只是碰到了它,我对现有的答案并不完全满意.关于铲子有很多好处,比连接+ =更快,但也有一个语义考虑.

@noodl接受的答案显示<<修改现有对象,而+ =创建一个新对象.因此,您需要考虑是否希望对字符串的所有引用都反映新值,或者您是否希望单独保留现有引用并创建一个新的字符串值以在本地使用.如果您需要所有引用以反映更新的值,那么您需要使用<<.如果你想单独留下其他引用,那么你需要使用+ =.

一个非常常见的情况是对字符串只有一个引用.在这种情况下,语义差异并不重要,因为它的速度,更喜欢<<.


gro*_*ser 10

因为它更快/不创建字符串的副本< - >垃圾收集器不需要运行.


Jos*_*Cho 5

虽然大多数答案的覆盖+=速度较慢,因为它会创建一个新副本,但重要的是要记住这一点+=并且<< 不可互换!您想在不同的情况下使用每个。

使用<<也将改变指向的任何变量b。在这里,我们也a可能不希望发生变异。

2.3.1 :001 > a = "hello"
 => "hello"
2.3.1 :002 > b = a
 => "hello"
2.3.1 :003 > b << " world"
 => "hello world"
2.3.1 :004 > a
 => "hello world"
Run Code Online (Sandbox Code Playgroud)

因为+=创建了一个新副本,所以它还会保留指向它的所有变量不变。

2.3.1 :001 > a = "hello"
 => "hello"
2.3.1 :002 > b = a
 => "hello"
2.3.1 :003 > b += " world"
 => "hello world"
2.3.1 :004 > a
 => "hello"
Run Code Online (Sandbox Code Playgroud)

当您处理循环时,理解这种区别可以为您省去很多麻烦!