我在研究 Ruby Koans。
关于字符串中的 test_the_shovel_operator_modifies_the_original_string Koan 包括以下评论:
test_the_shovel_operator_modifies_the_original_string
Ruby 程序员倾向于使用铲子操作符(< <)而不是加号 等于运算符(+ =)。为什么?
我猜这和速度有关,但我不明白引擎盖下的动作会让铲子操作员更快。
有人能解释一下这个偏好背后的细节吗?
因为它更快/不创建不需要运行的字符串 <-> 垃圾收集器的副本。
证据:
a = 'foo' a.object_id #=> 2154889340 a << 'bar' a.object_id #=> 2154889340 a += 'quux' a.object_id #=> 2154742560
因此,<<更改原始字符串而不是创建新字符串。这样做的原因是,在 ruby 中,a += b是 a = a + b(其他 <op>=操作符也是如此)的语法简写,a = a + b是赋值。另一方面,<<是 concat()的别名,它改变接收器的位置。
<<
a += b
a = a + b
<op>=
concat()
表现证明:
#!/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)
虽然不能直接回答您的问题,但是为什么 完全翻转的垃圾箱一直是我最喜欢的 Ruby 文章之一。它还包含关于垃圾收集的字符串的一些信息。
一个正在学习 Ruby 作为他的第一门编程语言的朋友在浏览 Ruby Koans 系列的 String in Ruby 时问了我同样的问题。我用下面的类比向他解释;
你有一杯半满的水,你需要重新装满你的杯子。
第一种方法是拿一个新的杯子,从水龙头中取出一半的水灌入,然后用第二个半满的杯子重新装满你的饮用水。你每次需要倒酒的时候都这样。
第二种方法是你拿起你的半满的杯子,直接从水龙头里加满水。
在一天结束的时候,如果你每次需要重新装满杯子的时候都选择一个新的杯子,你就会有更多的杯子需要清洗。
同样的情况也适用于铲工和正等操作工。另外,平等操作者每次需要重新装满玻璃杯的时候都会选择一个新的“玻璃杯”,而铲子操作者只是拿着同样的玻璃杯重新装满。在一天结束时,更多的’玻璃’收集为加平等运算符。
这是一个古老的问题,但我只是偶然发现它,我不完全满意现有的答案。铲子 < < 比连接 + = 快有很多优点,但也有语义上的考虑。
接受的答案显示 < < 在适当的位置修改现有对象,而 + = 创建一个新对象。因此,您需要考虑是希望对字符串的所有引用都反映新值,还是希望仅保留现有引用并创建一个本地使用的新字符串值。如果需要所有引用来反映更新后的值,则需要使用 < < 。如果希望不使用其他引用,那么需要使用 + = 。
一个非常常见的情况是,只有一个对字符串的引用。在这种情况下,语义差异并不重要,因为它的速度,所以自然而然地更喜欢 < < 。
虽然大多数答案涵盖 +=是较慢的,因为它创建一个新的副本,这是重要的是要记住,+=和 << 不是可互换!您希望在不同的情况下使用每种方法。
+=
使用 <<还将改变指向 b的任何变量。在这里,我们也突变 a时,我们可能不想要。
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"
因为 +=创建了一个新的副本,所以它也保留了所有指向它的变量。
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"
理解这种区别可以在处理循环时为您省去很多麻烦!