Skip to content

Instantly share code, notes, and snippets.

@mudge
Last active May 20, 2021 10:55
Show Gist options
  • Save mudge/a653a1fb837e108a5501937c456f4412 to your computer and use it in GitHub Desktop.
Save mudge/a653a1fb837e108a5501937c456f4412 to your computer and use it in GitHub Desktop.
A Ruby refinement to add methods to Enumerable for calculating the three Pythagorean means.
# A refinement to add methods to Enumerables for calculating the three
# Pythagorean means.
#
# See https://en.wikipedia.org/wiki/Pythagorean_means
module PythagoreanMeans
# Note that due to a bug refining modules in Ruby 2.7 [1], we can't `refine
# Enumerable` so we `refine Array` instead.
#
# See also https://interblah.net/why-is-nobody-using-refinements
#
# [1]: https://bugs.ruby-lang.org/issues/16852
refine Array do
def arithmetic_mean
(1.0 / length) * sum
end
alias_method :mean, :arithmetic_mean
alias_method :average, :arithmetic_mean
def geometric_mean
inject(:*)**(1.0 / length)
end
def harmonic_mean
Float(length) / sum { |x| 1.0 / x }
end
end
end
RSpec.describe PythagoreanMeans do
using PythagoreanMeans
describe '#arithmetic_mean' do
it 'returns the arithmetic mean of an array' do
expect([1, 2, 3, 4, 5].arithmetic_mean).to eq(3)
end
it 'is the greatest of the three means' do
xs = [1, 2, 3, 4, 5]
_least, _middle, greatest =
[xs.arithmetic_mean, xs.geometric_mean, xs.harmonic_mean].sort
expect(greatest).to eq(xs.arithmetic_mean)
end
end
describe '#mean' do
it 'is an alias for the arithmetic mean' do
expect([1, 2, 3, 4, 5].mean).to eq(3)
end
end
describe '#average' do
it 'is an alias for the arithmetic mean' do
expect([1, 2, 3, 4, 5].average).to eq(3)
end
end
describe '#geometric_mean' do
it 'returns the geometric mean of an array' do
expect([1, 2, 3, 4, 5].geometric_mean).to be_within(0.01).of(2.61)
end
it 'is in the middle of the three means' do
xs = [1, 2, 3, 4, 5]
_least, middle, _greatest =
[xs.arithmetic_mean, xs.geometric_mean, xs.harmonic_mean].sort
expect(middle).to eq(xs.geometric_mean)
end
end
describe '#harmonic_mean' do
it 'returns the harmonic mean of an array' do
expect([1, 2, 3, 4, 5].harmonic_mean).to be_within(0.01).of(2.18)
end
it 'is the least of the three means' do
xs = [1, 2, 3, 4, 5]
least, _middle, _greatest =
[xs.arithmetic_mean, xs.geometric_mean, xs.harmonic_mean].sort
expect(least).to eq(xs.harmonic_mean)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment