Bagaimana mendapatkan rata-rata dari array?
Jika saya memiliki array:
[0,4,8,2,5,0,2,6]
Rata-rata akan memberi saya 3,375.
Bagaimana mendapatkan rata-rata dari array?
Jika saya memiliki array:
[0,4,8,2,5,0,2,6]
Rata-rata akan memberi saya 3,375.
Jawaban:
Coba ini:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Perhatikan .to_f
, yang Anda inginkan untuk menghindari masalah dari divisi integer. Anda juga dapat melakukan:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
Anda dapat mendefinisikannya sebagai bagian dari Array
apa yang disarankan komentator lain, tetapi Anda harus menghindari pembagian integer atau hasil Anda akan salah. Juga, ini umumnya tidak berlaku untuk setiap jenis elemen yang mungkin (jelas, rata-rata hanya masuk akal untuk hal-hal yang dapat dirata-ratakan). Tetapi jika Anda ingin pergi ke rute itu, gunakan ini:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
Jika Anda belum pernah melihat inject
sebelumnya, itu tidak ajaib seperti yang mungkin muncul. Itu berulang atas setiap elemen dan kemudian menerapkan nilai akumulator untuknya. Akumulator kemudian diserahkan ke elemen berikutnya. Dalam hal ini, akumulator kami hanyalah bilangan bulat yang mencerminkan jumlah semua elemen sebelumnya.
Sunting: Komentator Dave Ray mengusulkan perbaikan yang bagus.
Sunting: Proposal komentator Glenn Jackman, menggunakan arr.inject(:+).to_f
, juga bagus tapi mungkin agak terlalu pintar jika Anda tidak tahu apa yang terjadi. Itu :+
adalah simbol; ketika melewati untuk menyuntikkan, itu berlaku metode yang dinamai oleh simbol (dalam hal ini, operasi penambahan) untuk setiap elemen terhadap nilai akumulator.
arr.inject(0.0) { |sum,el| sum + el } / arr.size
.
inject
antarmuka, yang disebutkan dalam dokumentasi. The to_proc
operator adalah &
.
Array#inject
berlebihan di sini. Gunakan saja #sum
. Misalnyaarr.sum.to_f / arr.size
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
Versi ini yang tidak digunakan instance_eval
adalah:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
instance_eval
memungkinkan Anda menjalankan kode sementara hanya menentukan a
sekali, sehingga dapat dirantai dengan perintah lain. Yaitu random_average = Array.new(10) { rand(10) }.instance_eval { reduce(:+) / size.to_f }
alih-alihrandom = Array.new(10) { rand(10) }; random_average = random.reduce(:+) / random.size
self
dalam blok itu, Anda akan mengalami masalah.) instance_eval
Lebih untuk metaprogramming atau DSL.
Saya percaya jawaban yang paling sederhana adalah
list.reduce(:+).to_f / list.size
reduce
adalah metode Enumerable
mixin yang digunakan oleh Array
. Dan terlepas dari namanya, saya setuju dengan @ ShuWu ... kecuali jika Anda menggunakan Rails yang mengimplementasikan sum
.
Saya berharap untuk Matematika. Rata-rata (nilai), tetapi tidak beruntung.
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
sum
metode, jadi ini tampaknya jawaban yang benar setelah 6 tahun, layak untuk penghargaan Nostradamus.
Versi Ruby> = 2.4 memiliki metode # jumlah Enumerable .
Dan untuk mendapatkan floating point rata-rata, Anda dapat menggunakan Integer # fdiv
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
Untuk versi yang lebih lama:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
Beberapa pembandingan solusi teratas (dalam urutan yang paling efisien):
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
daripada 0?
Biarkan saya membawa sesuatu ke dalam kompetisi yang memecahkan masalah divisi dengan nol:
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
Namun saya harus mengakui bahwa "coba" adalah penolong Rails. Tetapi Anda dapat dengan mudah menyelesaikan ini:
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
BTW: Saya pikir benar bahwa rata-rata daftar kosong adalah nol. Rata-rata tidak ada apa-apa, bukan 0. Jadi itulah perilaku yang diharapkan. Namun, jika Anda mengubah ke:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
hasil untuk Array kosong tidak akan menjadi pengecualian seperti yang saya harapkan tetapi mengembalikan NaN ... Saya belum pernah melihat itu sebelumnya di Ruby. ;-) Tampaknya menjadi perilaku khusus kelas Float ...
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
apa yang saya tidak suka tentang solusi yang diterima
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
adalah bahwa itu tidak benar-benar berfungsi secara murni fungsional. kita membutuhkan variabel arr untuk menghitung arr.size di akhir.
untuk menyelesaikan ini murni secara fungsional kita perlu melacak dua nilai: jumlah semua elemen, dan jumlah elemen.
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh meningkatkan solusi ini: alih-alih argumen r menjadi array, kita bisa menggunakan destrrukturisasi untuk segera mengambilnya menjadi dua variabel
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
jika Anda ingin melihat cara kerjanya, tambahkan beberapa put:
[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
Kita juga bisa menggunakan struct alih-alih array untuk mengandung jumlah dan penghitungan, tetapi kemudian kita harus mendeklarasikan struct terlebih dahulu:
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
digunakan di ruby, terima kasih untuk ini!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
Untuk hiburan publik, solusi lain:
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
.Saya melakukan hal yang sama cukup sering sehingga saya pikir lebih baik hanya memperpanjang Array
kelas dengan sederhanaaverage
metode . Ini tidak berfungsi untuk apa pun selain array angka seperti Integer atau Mengapung atau Desimal tetapi berguna ketika Anda menggunakannya dengan benar.
Saya menggunakan Ruby on Rails jadi saya menempatkan ini di config/initializers/array.rb
tetapi Anda dapat menempatkannya di mana saja yang termasuk dalam boot, dll.
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
Memecahkan pembagian dengan nol, pembagian integer dan mudah dibaca. Dapat dengan mudah dimodifikasi jika Anda memilih untuk mengembalikan array kosong 0.
Saya suka varian ini juga, tetapi sedikit lebih bertele-tele.
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
Metode ini dapat membantu.
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
Pendek tapi menggunakan variabel instan
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_size
daripada membuat variabel instan.
Anda dapat mencoba sesuatu seperti berikut ini:
a = [1,2,3,4,5]
# => [1, 2, 3, 4, 5]
(a.sum/a.length).to_f
# => 3.0