如何从Ruby数组创建平均值?

dot*_*tty 202 ruby ruby-on-rails

如何从阵列中找到平均值?

如果我有阵列:

[0,4,8,2,5,0,2,6]
Run Code Online (Sandbox Code Playgroud)

平均值会给我3.375.

谢谢!

Joh*_*lla 255

试试这个:

arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Run Code Online (Sandbox Code Playgroud)

请注意.to_f,您需要避免整数除法的任何问题.你也可以这样做:

arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
Run Code Online (Sandbox Code Playgroud)

您可以将其定义Array为另一个评论者建议的一部分,但您需要避免整数除法,否则您的结果将是错误的.此外,这通常不适用于每种可能的元素类型(显然,平均值仅对可以平均的事物有意义).但是如果你想走那条路,那就用这个:

class Array
  def sum
    inject(0.0) { |result, el| result + el }
  end

  def mean 
    sum / size
  end
end
Run Code Online (Sandbox Code Playgroud)

如果你以前没见过inject,那就不像它看起来那么神奇了.它迭代每个元素,然后将累加器值应用于它.然后将累加器传递给下一个元素.在这种情况下,我们的累加器只是一个整数,它反映了所有先前元素的总和.

编辑:评论家戴夫雷提出了一个很好的改进.

编辑:评论者格伦杰克曼的提议,使用arr.inject(:+).to_f,也很好,但如果你不知道发生了什么,也许有点太聪明了.这:+是一个象征; 当传递给inject时,它将符号命名的方法(在本例中为加法运算)应用于累加器值的每个元素.

  • 或者:arr.inject(:+).to_f/arr.size#=> 3.375 (101认同)
  • 如果你正在使用Rails,那么`Array#inject`在这里是过度的.只需使用`#sum`.例如`arr.sum.to_f/arr.size` (20认同)
  • @John:这不完全是Symbol#to_proc转换 - 它是文档中提到的`inject`接口的一部分.`to_proc`运算符是`&`. (8认同)
  • 你可以消除to_f和?运算符通过将初始值传递给inject:`arr.inject(0.0){| sum,el | sum + el}/arr.size`. (5认同)
  • 我不认为这需要添加到Array类,因为它不能推广到Arrays可以包含的所有类型. (5认同)

Cor*_*ook 109

a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
Run Code Online (Sandbox Code Playgroud)

不使用的版本instance_eval是:

a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
Run Code Online (Sandbox Code Playgroud)

  • `instance_eval`允许你运行代码,只指定一次'a`,所以它可以与其他命令链接.即`random_average = Array.new(10){rand(10)}.instance_eval {reduce(:+)/ size.to_f}`而不是`random = Array.new(10){rand(10)}; random_average = random.reduce(:+)/ random.size` (10认同)
  • 我认为它不太聪明.我认为它解决了这个问题.即,它使用reduce,这是完全正确的.应该鼓励程序员理解什么是正确的,为什么是正确的,然后传播.对于平均,真实的微不足道的操作,人们不需要"聪明".但是,通过了解"减少"对于一个微不足道的案例,人们可以开始将其应用于更复杂的问题.给予好评. (4认同)
  • 为什么需要instance_eval呢? (3认同)
  • 我不知道,使用instance_eval这种方式看起来很奇怪,并且它有很多与之相关的问题,这使得这种方法成为一个坏主意,IMO.(例如,如果您尝试访问和实例变量或该块内的`self`上的方法,则会遇到问题.)`instance_eval`更适用于元编程或DSL. (2认同)

小智 92

我相信最简单的答案是

list.reduce(:+).to_f / list.size
Run Code Online (Sandbox Code Playgroud)


Den*_*ham 47

我希望Math.average(值),但没有这样的运气.

values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
Run Code Online (Sandbox Code Playgroud)

  • 在2016年圣诞节之后(Ruby 2.4),Array _will_有一个"sum"方法,所以这看起来是6年后的正确答案,值得诺查丹玛斯奖. (10认同)
  • 我认为这可能是一个Rails扩展,所以不是一般的ruby案例答案.无论如何,仍然给你一个upvote. (8认同)
  • 我没有意识到#sum是由Rails添加的!感谢您指出了这一点. (3认同)

San*_*osh 34

Ruby版本> = 2.4有一个Enumerable#sum方法.

要获得浮点平均值,可以使用Integer#fdiv

arr = [0,4,8,2,5,0,2,6]

arr.sum.fdiv(arr.size)
# => 3.375
Run Code Online (Sandbox Code Playgroud)

对于旧版本:

arr.reduce(:+).fdiv(arr.size)
# => 3.375
Run Code Online (Sandbox Code Playgroud)


ste*_*iel 7

最佳解决方案的一些基准测试(以最有效的顺序):

大数组:

array = (1..10_000_000).to_a

Benchmark.bm do |bm|
  bm.report { array.instance_eval { reduce(:+) / size.to_f } }
  bm.report { array.sum.fdiv(array.size) }
  bm.report { array.sum / array.size.to_f }
  bm.report { array.reduce(:+).to_f / array.size }
  bm.report { array.reduce(:+).try(:to_f).try(:/, array.size) }
  bm.report { array.inject(0.0) { |sum, el| sum + el }.to_f / array.size }
  bm.report { array.reduce([ 0.0, 0 ]) { |(s, c), e| [ s + e, c + 1 ] }.reduce(:/) }
end


    user     system      total        real
0.480000   0.000000   0.480000   (0.473920)
0.500000   0.000000   0.500000   (0.502158)
0.500000   0.000000   0.500000   (0.508075)
0.510000   0.000000   0.510000   (0.512600)
0.520000   0.000000   0.520000   (0.516096)
0.760000   0.000000   0.760000   (0.767743)
1.530000   0.000000   1.530000   (1.534404)
Run Code Online (Sandbox Code Playgroud)

小阵列:

array = Array.new(10) { rand(0.5..2.0) }

Benchmark.bm do |bm|
  bm.report { 1_000_000.times { array.reduce(:+).to_f / array.size } }
  bm.report { 1_000_000.times { array.sum / array.size.to_f } }
  bm.report { 1_000_000.times { array.sum.fdiv(array.size) } }
  bm.report { 1_000_000.times { array.inject(0.0) { |sum, el| sum + el }.to_f / array.size } }
  bm.report { 1_000_000.times { array.instance_eval { reduce(:+) / size.to_f } } }
  bm.report { 1_000_000.times { array.reduce(:+).try(:to_f).try(:/, array.size) } }
  bm.report { 1_000_000.times { array.reduce([ 0.0, 0 ]) { |(s, c), e| [ s + e, c + 1 ] }.reduce(:/) } }
end


    user     system      total        real
0.760000   0.000000   0.760000   (0.760353)
0.870000   0.000000   0.870000   (0.876087)
0.900000   0.000000   0.900000   (0.901102)
0.920000   0.000000   0.920000   (0.920888)
0.950000   0.000000   0.950000   (0.952842)
1.690000   0.000000   1.690000   (1.694117)
1.840000   0.010000   1.850000   (1.845623)
Run Code Online (Sandbox Code Playgroud)


Dor*_*ian 5

无需重复数组(例如,非常适合单行):

[1, 2, 3, 4].then { |a| a.sum.to_f / a.size }
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢“.then”! (2认同)