どのように配列から平均を見つけるのでしょうか?
配列がある場合:
[0,4,8,2,5,0,2,6]
平均すると3.375になります。
どのように配列から平均を見つけるのでしょうか?
配列がある場合:
[0,4,8,2,5,0,2,6]
平均すると3.375になります。
回答:
これを試して:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
.to_f
整数の除算による問題を回避するために必要なに注意してください。あなたも行うことができます:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
Array
別のコメンターが提案したように、それをの一部として定義できますが、整数除算を回避する必要があります。また、これは一般的にすべての可能な要素タイプに適用できるわけではありません(明らかに、平均化は平均化できるものに対してのみ意味があります)。しかし、あなたがそのルートに行きたいなら、これを使ってください:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
あなたがinject
前に見たことがないなら、それは現れるかもしれないほど魔法ではありません。各要素を反復処理し、アキュムレータ値を適用します。その後、アキュムレータは次の要素に渡されます。この場合、アキュムレータは、前のすべての要素の合計を反映する単なる整数です。
編集:コメント投稿者のDave Rayが素晴らしい改善を提案しました。
編集:コメンターGlenn Jackmanの提案(を使用arr.inject(:+).to_f
)もいいですが、何が起こっているのかわからない場合は、少し賢すぎるかもしれません。:+
シンボルです。injectに渡されると、シンボルで指定されたメソッド(この場合は加算演算)が各要素にアキュムレータ値に対して適用されます。
arr.inject(0.0) { |sum,el| sum + el } / arr.size
。
inject
は、ドキュメントで言及されているインターフェースの一部です。to_proc
オペレータがあります&
。
Array#inject
ここではやりすぎです。だけを使用してください#sum
。例えばarr.sum.to_f / arr.size
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
これを使用しないバージョンは次のようにinstance_eval
なります:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
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
self
そのブロックの内部でインスタンス変数またはメソッドにアクセスしようとすると、問題が発生します。)instance_eval
メタプログラミングまたはDSLの方が適しています。
最も簡単な答えは
list.reduce(:+).to_f / list.size
reduce
がEnumerable
使用するミックスインの方法ですArray
。そして、その名前にもかかわらず、@ ShuWuに同意します...を実装するRailsを使用している場合を除きますsum
。
私はMath.average(values)を期待していましたが、そのような運はありませんでした。
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
sum
メソッドがあるため、これは6年後の正解であり、ノストラダムス賞に値するものです。
Rubyバージョン> = 2.4にはEnumerable#sumメソッドがあります。
浮動小数点平均を取得するには、Integer#fdivを使用できます
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
古いバージョンの場合:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
上位のソリューションのベンチマーク(最も効率的な順に):
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)
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)
class Array
def sum
inject( nil ) { |sum,x| sum ? sum+x : x }
end
def mean
sum.to_f / size.to_f
end
end
[0,4,8,2,5,0,2,6].mean
nil
0ではなく合計である必要があるのはなぜですか?
ゼロ除算問題を解決するものを競争に持ち込みましょう:
a = [1,2,3,4,5,6,7,8]
a.reduce(:+).try(:to_f).try(:/,a.size) #==> 4.5
a = []
a.reduce(:+).try(:to_f).try(:/,a.size) #==> nil
ただし、「試してみる」はRailsのヘルパーであることを認めざるを得ません。しかし、これは簡単に解決できます。
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
ところで:空のリストの平均がゼロであることは正しいと思います。何もないことの平均は0ではなく、何もありません。したがって、これは予想される動作です。ただし、次のように変更した場合:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
空の配列の結果は期待どおりの例外ではありませんが、代わりにNaNを返します... Rubyではこれまで見たことはありません。;-) Floatクラスの特別な動作のようです...
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
受け入れられた解決策について私が好きではないこと
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
それは純粋に機能的な方法では実際には機能しないということです。最後にarr.sizeを計算するには、変数arrが必要です。
これを純粋に機能的に解決するには、すべての要素の合計と要素の数という2つの値を追跡する必要があります。
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhoshはこのソリューションを改善しました。引数rが配列である代わりに、構造化を使用してすぐに2つの変数に分解できます。
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
それがどのように機能するかを見たい場合は、いくつかのプットを追加してください:
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
r2 = [ sum + ele, size + 1 ]
puts "adding #{ele} gives #{r2}"
r2
end.inject(:/)
adding 5 gives [5.0, 1]
adding 6 gives [11.0, 2]
adding 7 gives [18.0, 3]
adding 8 gives [26.0, 4]
=> 6.5
配列の代わりに構造体を使用して合計とカウントを含めることもできますが、その場合は最初に構造体を宣言する必要があります。
R=Struct.new(:sum, :count)
[5, 6, 7, 8].inject( R.new(0.0, 0) ) do |r,ele|
r.sum += ele
r.count += 1
r
end.inject(:/)
end.method
ルビーで使われるのを見るのは初めてです、ありがとうございます!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
公共の娯楽のために、さらに別の解決策:
a = 0, 4, 8, 2, 5, 0, 2, 6
a.reduce [ 0.0, 0 ] do |(s, c), e| [ s + e, c + 1 ] end.reduce :/
#=> 3.375
Array#average
ます。同じことを頻繁に行っていたのでArray
、単純なaverage
メソッドでクラスを拡張するのが賢明だと思いました。Integers、Floats、Decimalsなどの数値の配列以外には機能しませんが、正しく使用すると便利です。
私はRuby on Railsを使用しているので、これを配置しましたconfig/initializers/array.rb
が、ブートなどに含まれる任意の場所に配置できます。
config/initializers/array.rb
class Array
# Will only work for an Array of numbers like Integers, Floats or Decimals.
#
# Throws various errors when trying to call it on an Array of other types, like Strings.
# Returns nil for an empty Array.
#
def average
return nil if self.empty?
self.sum / self.size
end
end
a = [0,4,8,2,5,0,2,6]
sum = 0
a.each { |b| sum += b }
average = sum / a.length
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : a.reduce(:+)/a.size.to_f
=> 3.375
ゼロ除算、整数除算を解決し、読みやすいです。空の配列が0を返すように選択すると、簡単に変更できます。
私もこの亜種が好きですが、少し冗長です。
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : [a.reduce(:+), a.size.to_f].reduce(:/)
=> 3.375
この方法は役に立ちます。
def avg(arr)
val = 0.0
arr.each do |n|
val += n
end
len = arr.length
val / len
end
p avg([0,4,8,2,5,0,2,6])
[1,2].tap { |a| @asize = a.size }.inject(:+).to_f/@asize
短いがインスタンス変数を使用
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_size
インスタンス変数を作成するのではなく、実行します。