根据标准对arraylist值求和

时间:2013-01-21 18:40:37

标签: ruby

给定数组[X,Y]的数组:

a=[[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]

2<=X<4的所有Y位数求和的最有效方法是什么?

5 个答案:

答案 0 :(得分:4)

我正在使用它:

a.select{ |x,y| (2...4) === x }.inject(0){ |m, (x,y)| m + y }
=> 4

我并不喜欢使用...,因为它会让人们误解它的工作方式。以下是一些等效的测试方法:

a.select{ |x,y| (2..3) === x }.inject(0){ |m, (x,y)| m + y }
ary.select{ |x,y| (2 <= x) && (x < 4) }.inject(0){ |m, (x,y)| m + y } } }

这里有一些基准代码:

require 'benchmark'

a = [ [1,2], [2,2], [3,2], [4,2], [5,2], [6,2] ]
n = 1_000_000

Benchmark.bm(12) do |b|
  b.report('The Tin Man')  { n.times { a.select{ |x,y| (2...4) === x }.inject(0){ |m, (x,y)| m + y } } }
  b.report('The Tin Man2') { n.times { a.select{ |x,y| (2 <= x) && (x < 4) }.inject(0){ |m, (x,y)| m + y } } }
  b.report('Mik_Die')      { n.times { a.select{ |i| (2...4).include? i[0] }.map(&:last).reduce(:+) } }
  b.report('Justin Ko')    { n.times { a.inject(0){ |sum, coord| (coord[0] >= 2  and coord[0] < 4) ? sum + coord[1] : sum } } }
  b.report('Justin Ko2')   { n.times { a.inject(0){ |sum, (x,y)| (x >= 2  and x < 4) ? sum + y : sum } } }
  b.report('Leo Correa')   { n.times { sum = 0; a.each { |x, y| sum += y if x >= 2 and x < 4 } } }
  b.report('tokland')      { n.times { a.map { |x, y| y if x >= 2 && x < 4 }.compact.inject(0, :+) } }
end

及其输出:

                   user     system      total        real
The Tin Man    4.020000   0.000000   4.020000 (  4.020154)
The Tin Man2   2.420000   0.000000   2.420000 (  2.424424)
Mik_Die        3.830000   0.000000   3.830000 (  3.836531)
Justin Ko      2.070000   0.000000   2.070000 (  2.072446)
Justin Ko2     2.000000   0.000000   2.000000 (  2.035079)
Leo Correa     1.260000   0.000000   1.260000 (  1.259672)
tokland        2.650000   0.010000   2.660000 (  2.645466)

这里学到的教训是inject代价高昂。

答案 1 :(得分:3)

我会使用inject

a = [[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]
sum = a.inject(0){ |sum, (x,y)| (x >= 2  and x < 4) ? sum + y : sum }
puts sum
#=> 4

rdoc很好地描述了inject方法:

  

注入(初始){|备忘录,obj |阻止}→obj

     

通过应用二进制操作组合枚举的所有元素,   由命名方法或运算符的块或符号指定。

     

如果指定一个块,则对于枚举中的每个元素,该块为   传递了累加器值(memo)和元素。如果你指定一个   而是符号,然后将集合中的每个元素传递给   备忘录的命名方法。在任何一种情况下,结果都成为新的   备忘录的价值。在迭代结束时,备忘录的最终值   是方法的返回值。

     

如果没有明确指定备忘录的初始值,则使用   第一个集合元素用作备忘录的初始值。

更新 - 基准数组与拆包:

@tokland建议拆开这些配对,这肯定会提高可读性。运行以下基准测试以查看它是否比使用阵列更快(即我的原始解决方案)。

require 'benchmark'

a = [ [1,2], [2,2], [3,2], [4,2], [5,2], [6,2] ]
n = 2_000_000

Benchmark.bm(12) do |b|
  b.report('array'){n.times{a.inject(0){ |sum, coord| (coord[0] >= 2  and coord[0] < 4) ? sum + coord[1] : sum }}}
  b.report('unpacked'){n.times{a.inject(0){ |sum, (x,y)| (x >= 2  and x < 4) ? sum + y : sum }}}
end

给出了结果

                   user     system      total        real
array          3.916000   0.000000   3.916000 (  3.925393)
unpacked       3.619000   0.000000   3.619000 (  3.616361)

所以,至少在这种情况下,拆包对更好。

答案 2 :(得分:1)

我喜欢@JustinKo给出的注入答案,但是如果你是Ruby的新手,这里的另一个解决方案可能更容易理解。

a=[[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]
sum = 0
a.each { |x, y| sum += y if x >= 2 and x < 4 }
puts sum
#=> 4

答案 3 :(得分:0)

使用更简单方法的链条在ruby中更明显。所以:

a=[[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]
a.select{ |i| (2...4).include? i[0] }.map(&:last).reduce(:+)
# => 4 

答案 4 :(得分:0)

从概念上讲,您想要使用的是列表理解。唉,Ruby没有LC的内置语法,但是一个紧凑的+ map可以很好地完成工作:

a.map { |x, y| y if x >= 2 && x < 4 }.compact.inject(0, :+)
#=> 4

如果你正在编写一个中/大脚本,你可能已经(并且应该有)扩展 模块。添加所需的方法,以便编写声明性和简洁的代码:

a.map_select { |x, y| y if x >= 2 && x < 4 }.sum

甚至:

a.sum { |x, y| y if x >= 2 && x < 4 }