b1 = Time.now
puts (1..100000).inject(0) { |x, y| x + y }
a1 = Time.now
puts "Time for inject: #{a1 - b1}"
b2 = Time.now
sum = 0
(1..100000).each do |value|
sum += value
end
puts sum
a2 = Time.now
puts "Time for each: #{a2 - b2}"
上面的Ruby代码比较了两种求和整数的方法。令我惊讶的是,更优雅的 inject 或 reduce 方法胜过另一方。为什么会这样?为什么人们会使用低效的注入或 reduce ?只是因为它很优雅?
PS:感谢所有鼓舞人心的答案。我的目的是询问导致这些差异的幕后情况。
答案 0 :(得分:6)
在这种情况下我会用一点点数学:
require "benchmark"
N = 5_000_000
Benchmark.bmbm do |bm|
bm.report "inject 1" do
(1..N).inject(0) { |x, y| x + y }
end
bm.report "inject 2" do
(1..N).inject(:+)
end
bm.report "each" do
sum = 0
(1..N).each do |value|
sum += value
end
end
bm.report "sum of finite arithmetic progression" do
((1 + N) * N) / 2
end
end
结果是:
% ruby sum.rb
Rehearsal ------------------------------------------------------------------------
inject 1 0.500000 0.000000 0.500000 ( 0.507497)
inject 2 0.320000 0.000000 0.320000 ( 0.322675)
each 0.370000 0.000000 0.370000 ( 0.380504)
sum of finite arithmetic progression 0.000000 0.000000 0.000000 ( 0.000005)
--------------------------------------------------------------- total: 1.190000sec
user system total real
inject 1 0.500000 0.000000 0.500000 ( 0.507697)
inject 2 0.320000 0.000000 0.320000 ( 0.322323)
each 0.370000 0.000000 0.370000 ( 0.380307)
sum of finite arithmetic progression 0.000000 0.000000 0.000000 ( 0.000004)
%
更好的数学总是更快:)
答案 1 :(得分:5)
是的,代码可读性比微优化更重要。即使采用数百万元素的总和,差异也几乎不可察觉。此外,两种方法都是O(n)
,因此随着元素数量的增加,两种方法都不会明显优于另一种方法。
正如其他人所指出的,inject(:+)
还要快一点。即使它不是,选择最容易看到的那个,并且不要担心性能上的细微差别。这可能不是您申请中的瓶颈。
require "benchmark"
N = 5_000_000
Benchmark.bmbm do |bm|
bm.report "inject 1" do
(1..N).inject(0) { |x, y| x + y }
end
bm.report "inject 2" do
(1..N).inject(:+)
end
bm.report "each" do
sum = 0
(1..N).each do |value|
sum += value
end
end
end
结果:
user system total real
inject 1 0.610000 0.000000 0.610000 ( 0.613080)
inject 2 0.370000 0.000000 0.370000 ( 0.370892)
each 0.570000 0.000000 0.570000 ( 0.568266)
答案 2 :(得分:3)
请尝试以下方法:
puts (1..100000).inject(:+)
就我个人而言,如果单线注入可以替换3条线,只要它不会变得混乱,我会选择注射。
答案 3 :(得分:3)
@derp是对的。我建议你下次使用基准模块,如:
#!/usr/bin/env ruby
require "benchmark"
Benchmark.bm do |x|
x.report { (1..10000000).inject(:+) }
x.report { sum = 0; (1..10000000).each { |value| sum += value } }
end
答案 4 :(得分:2)
值得注意的是,大多数或所有之前的答案可能都是最新的主要版本的ruby(1.9)。在1.8.7中,这种差异更明显:
$ ruby -v
ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin10.6.0], MBARI 0x6770, Ruby Enterprise Edition 2011.03
$ ruby bench.rb
Rehearsal ------------------------------------------------------------------------
inject 1 3.910000 0.010000 3.920000 ( 3.932388)
inject 2 0.660000 0.000000 0.660000 ( 0.662330)
each 1.120000 0.010000 1.130000 ( 1.126276)
sum of finite arithmetic progression 0.000000 0.000000 0.000000 ( 0.000009)
--------------------------------------------------------------- total: 5.710000sec
user system total real
inject 1 3.930000 0.010000 3.940000 ( 3.956084)
inject 2 0.680000 0.000000 0.680000 ( 0.685073)
each 1.110000 0.000000 1.110000 ( 1.109675)
sum of finite arithmetic progression 0.000000 0.000000 0.000000 ( 0.000009)
绝对同意可读性&维护更重要。
答案 5 :(得分:1)
Ruby主要不是关于表演。如果你想要表演,可以选择其他语言。
Ruby非常乐意编写优雅的代码:)