Bir diziden ortalama nasıl bulunur?
Ben dizi varsa:
[0,4,8,2,5,0,2,6]
Ortalama bana 3.375 verir.
Bir diziden ortalama nasıl bulunur?
Ben dizi varsa:
[0,4,8,2,5,0,2,6]
Ortalama bana 3.375 verir.
Yanıtlar:
Bunu dene:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
.to_f
Tamsayı bölümünden herhangi bir sorundan kaçınmak isteyeceğinizi not edin . Ayrıca şunları da yapabilirsiniz:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
Bunu Array
başka bir yorumcunun önerdiği şekilde tanımlayabilirsiniz , ancak tamsayı bölünmesinden kaçınmanız gerekir, aksi takdirde sonuçlarınız yanlış olur. Ayrıca, bu genellikle olası her öğe türü için geçerli değildir (açıkçası, ortalama yalnızca ortalaması alınabilecek şeyler için anlamlıdır). Ancak bu rotaya gitmek istiyorsanız, şunu kullanın:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
Daha inject
önce görmediyseniz , göründüğü kadar büyülü değildir. Her öğenin üzerinde yinelenir ve sonra ona bir akümülatör değeri uygular. Akümülatör daha sonra bir sonraki elemana verilir. Bu durumda, akümülatörümüz önceki tüm öğelerin toplamını yansıtan bir tamsayıdır.
Edit: Yorumcu Dave Ray hoş bir gelişme teklif etti.
Edit: Yorumcu Glenn Jackman, kullanarak arr.inject(:+).to_f
, önerisi de güzel ama ne olduğunu bilmiyorsanız belki biraz zekice. :+
Bir semboldür; enjekte edildiğinde, akümülatör değerine karşı her öğeye sembol (bu durumda toplama işlemi) adlı yöntemi uygular.
arr.inject(0.0) { |sum,el| sum + el } / arr.size
.
inject
dokümantasyonda belirtilen arayüzün bir parçası . to_proc
Operatörüdür &
.
Array#inject
burada aşırı dolu. Sadece kullan #sum
. Örneğinarr.sum.to_f / arr.size
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
Bunun kullanmayan bir sürümü instance_eval
:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
instance_eval
yalnızca bir a
kez belirtirken kodu çalıştırmanızı sağlar , böylece diğer komutlarla zincirlenebilir. Yani random_average = Array.new(10) { rand(10) }.instance_eval { reduce(:+) / size.to_f }
yerinerandom = Array.new(10) { rand(10) }; random_average = random.reduce(:+) / random.size
self
bu bloğun içindeki bir yönteme erişmeye ve örnek oluşturmaya çalıştıysanız , sorun instance_eval
yaşarsınız .) Meta programlama veya DSL için daha fazladır.
En basit cevabın
list.reduce(:+).to_f / list.size
reduce
bir yöntemidir . Ve ismine rağmen, @ShuWu'ya katılıyorum ... uygulayan Rails'i kullanmadığınız sürece . Enumerable
Array
sum
Math.average (değerleri) umuyordum, ama böyle bir şans yok.
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
sum
, bu nedenle Nostradamus ödülüne layık olan 6 yıl sonra doğru bir cevap gibi görünüyor.
Ruby sürümleri> = 2.4, Numaralandırılabilir # toplam yöntemine sahiptir.
Kayan nokta ortalaması almak için Integer # fdiv kullanabilirsiniz
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
Eski sürümler için:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
En iyi çözümlerin bazı kıyaslamaları (en verimli sırayla):
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 yerine toplamalı?
Ayrımı sıfır sorunla çözen bir şeyi rekabete sokayım:
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
Ancak, "denemek" bir Rails yardımcısı olduğunu itiraf etmeliyim. Ancak bunu kolayca çözebilirsiniz:
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
BTW: Boş bir listenin ortalamasının sıfır olduğunu düşünüyorum. Hiçbir şeyin ortalaması hiçbir şey değildir, 0 değildir. Yani beklenen davranış budur. Ancak, şu şekilde değiştirirseniz:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
boş Diziler için sonuç beklediğim gibi bir istisna olmayacak, ama bunun yerine NaN döndürüyor ... Daha önce hiç Ruby'de görmedim. ;-) Float sınıfının özel bir davranışı gibi görünüyor ...
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
kabul edilen çözüm hakkında sevmediğim şey
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
aslında tamamen işlevsel bir şekilde çalışmadığıdır. sonunda arr.size değerini hesaplamak için değişken bir arr'ye ihtiyacımız var.
Bunu tamamen işlevsel olarak çözmek için iki değeri izlememiz gerekir: tüm elemanların toplamı ve eleman sayısı.
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh bu çözüm üzerinde gelişti: r argümanı bir dizi olmak yerine, yıkımı derhal iki değişkene ayırmak için kullanabiliriz
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
nasıl çalıştığını görmek istiyorsanız, aşağıdakileri ekleyin:
[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
Toplamı ve sayımı içermek için dizi yerine bir yapı da kullanabiliriz, ancak önce yapıyı bildirmeliyiz:
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
Ruby'de ilk kez kullandığımı görüyorum , bunun için teşekkürler!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
Halka açık eğlence için, başka bir çözüm:
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
.Aynı şeyi sık sık yapıyordum, bu yüzden Array
sınıfı basit bir average
yöntemle genişletmenin ihtiyatlı olduğunu düşündüm . Tamsayılar, Kayan Sayılar veya Ondalık Sayılar dizisi dışında hiçbir şey için çalışmaz, ancak doğru kullandığınızda kullanışlıdır.
Ruby on Rails kullanıyorum, bu yüzden bunu yerleştirdim, config/initializers/array.rb
ancak önyüklemede bulunan herhangi bir yere yerleştirebilirsiniz, vb.
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
Bölmeyi sıfıra, tamsayı bölümüne böler ve okunması kolaydır. Boş bir dizi 0 döndürmeyi seçerseniz kolayca değiştirilebilir.
Bu varyantı da seviyorum, ama biraz daha garip.
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : [a.reduce(:+), a.size.to_f].reduce(:/)
=> 3.375
arr = [0,4,8,2,5,0,2,6]
average = arr.inject(&:+).to_f / arr.size
# => 3.375
Bu yöntem yardımcı olabilir.
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
Kısa ancak örnek değişkeni kullanılıyor
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_size
Bir örnek değişkeni oluşturmak yerine bunu yapardım .
Aşağıdaki gibi bir şey deneyebilirsiniz:
a = [1,2,3,4,5]
# => [1, 2, 3, 4, 5]
(a.sum/a.length).to_f
# => 3.0