我希望Ruby的解析器会进行这种微不足道的优化,但似乎没有(谈到YARV实现,Ruby 1.9.x,2.0.0):
require 'benchmark'
def fib1
a, b = 0, 1
while b < 9999**4000
a, b = b, a+b
end
puts "tdone !"
end
MAX_FIB = 9999**4000
def fib2
a, b = 0, 1
while b < MAX_FIB
a, b = b, a+b
end
puts "tdone !"
end
if __FILE__ == $0
Benchmark.bm do |r|
r.report('plain') { fib1 }
r.report('predefined constant') { fib2 }
end
end
plain done !
32.810000 0.220000 33.030000 ( 33.069854)
predefined constant done !
0.120000 0.000000 0.120000 ( 0.119303)
由于这两种方法都是相同的,除了使用预定义的常量而不是第二个常量表达式,因此 Ruby 解释器似乎在每个循环中一次又一次地计算功率常数。
Ruby 根本不做这个基本优化或只是在某些特定情况下有什么材料吗?
抱歉给出另一个答案,但我不想删除或编辑我以前的答案,因为它下面的有趣讨论。
正如 Jörg W Mittag 所说,Ruby 每次都必须计算给while
表达式,因为根本不可能说它是恒定的。举一个极端的例子,我想出了以下代码:
class Fixnum
alias :original_plus :+
@@increment = 0
def self.increase!
@@increment = @@increment.original_plus 1
end
def +(other)
(original_plus(other)).original_plus(@@increment)
end
end
while 1+1 < 5
puts Fixnum.increase!
end
puts 'done'
# 1
# 2
# 3
# done
尝试:
def fib1
a, b = 0, 1
while b < 9999**4000
a, b = b, a+b
end
puts "tdone !"
end
def fib2
a, b = 0, 1
value = 9999**4000
while b < value
a, b = b, a+b
end
puts "tdone !"
end
不会有区别的!
原因是在第一次测试中,Ruby 在你调用的方法内部计算9999**4000
而第二个测试使用的是在外部(在 Benchmark 启动之前)计算的常量,因此你的第二个结果在 '9999**4000' 计算时间更短。
更新:嗯,这两个测试之间实际上有很大的差异,Ruby 可以立即计算出 9999**4000 - 请参阅下面的@JorgWMittag评论。
我删除这个答案并不是为了让评论保持活力。