Рубиновый путь: деление на ноль
У меня есть следующий способ вычисления среднего значения:
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
average.round(2)
end
Это ничего особенного, но у меня есть проблема, которую я ожидаю от всех средних уравнений: она может делиться на ноль, если входы все равны нулю.
Итак, я подумал об этом:
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
if total==0
average = 0.00
else
average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
average.round(2)
end
end
... и это работает, но для меня это чувствует. Есть ли более элегантный "Ruby Way", чтобы избежать этого деления по нулевой проблеме?
Я хочу, чтобы у меня был оператор "если только", например...
average = numerator / denominator unless denominator == 0 then 0
Любые предложения?
Ответы
Ответ 1
Вы можете использовать nonzero?
, как в:
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
average = [a, 2*b, 3*c, 4*d, 5*e].sum / (total.nonzero? || 1)
end
Больше людей будут более знакомы с использованием тернарного оператора (total == 0 ? 1 : total)
, так что другая возможность.
Ответ 2
Обычно для включения исключения используется rescue
, а затем для возврата значения по умолчанию:
def compute_average(a, b, c, d, e)
total = [a, b, c, d, e].sum.to_f
average = [ a, 2*b, 3*c, 4*d, 5*e ].sum / total
average.round(2)
rescue ZeroDivisionError
0.0
end
Также я бы написал:
average = numerator / denominator unless denominator == 0 then 0
а
average = (denominator == 0) ? 0 : numerator / denominator
Ответ 3
Для меня самый чистый способ:
numerator / denominator rescue 0
Это также избавляет вас от обработки 0 / 0
.
Как указывает @Andrew, это справедливо только для целых чисел. См. Комментарии к этому ответу для получения дополнительной информации.
Ответ 4
Пока это устаревшая нить, я подумал, что буду звонить с помощью простого одного лайнера, который вы можете использовать...
@average = variable1 / variable2 rescue 0
Ответ 5
def compute_average(a,b,c,d,e)
total = (a+b+c+d+e).to_f
total.zero? ? 0 : ((a + 2*b + 3*c + 4*d + 5*e) / total).round(2)
end
Ответ 6
TL; DR: одно возможное решение
def compute_average(*values)
# This makes sure arrays get flattened to a single array.
values.flatten!
# Throws away all nil values passed as arguments.
values.reject!(&:nil?)
# Throws away all non-numeric values.
# This includes trashing strings that look like numbers, like "12".
values.keep_if{ |v| v.is_a? Numeric }
total = values.sum.to_f
return Float::NAN if total.zero?
# I'm not sure what this business is
# average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
# but it can be translated to
average = values.each_with_index.map{ |v,i| v*(i+1) }.sum / total
average.round(2)
end
Это защищает от всех случаев:
compute_average(1,2,3,4,5)
=> 3.67
compute_average(0,0,0,0,0)
=> NaN
compute_average(1,2,nil,4,5)
=> 3.08
compute_average(1,2,"string",4,5)
=> 3.08
compute_average(1)
=> 1.0
compute_average([1,2,3,4,5])
=> 3.67
compute_average
=> NaN
Функция оригинала:
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
average.round(2)
end
Рассмотрим проверку нуля:
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
return if total.zero?
average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
average.round(2)
end
Это изменение защищает только один случай:
compute_average(1,2,3,4,5)
# => 3.67
compute_average(0,0,0,0,0)
# => nil
compute_average(1,2,nil,4,5)
# => TypeError: NilClass can't be coerced into Fixnum
compute_average(1,2,"string",4,5)
# => TypeError: String can't be coerced into Fixnum
compute_average(1)
# => ArgumentError: wrong number of arguments calling `compute_average` (1 for 5)
compute_average([1,2,3,4,5])
# => ArgumentError: wrong number of arguments calling `compute_average` (1 for 5)
compute_average
# => ArgumentError: wrong number of arguments calling `compute_average` (0 for 5)
Рассмотрим использование встроенного rescue
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
average = [a, 2*b, 3*c, 4*d, 5*e].sum / total rescue 0
average.round(2)
end
Это изменение защищает только один случай:
compute_average(1,2,3,4,5)
# => 3.67
compute_average(0,0,0,0,0)
# => NaN
compute_average(1,2,nil,4,5)
# => TypeError: NilClass can't be coerced into Fixnum
compute_average(1,2,"string",4,5)
# => TypeError: String can't be coerced into Fixnum
compute_average(1)
# => ArgumentError: wrong number of arguments calling `compute_average` (1 for 5)
compute_average([1,2,3,4,5])
# => ArgumentError: wrong number of arguments calling `compute_average` (1 for 5)
compute_average
# => ArgumentError: wrong number of arguments calling `compute_average` (0 for 5)
Использование встроенного rescue
имеет другое следствие. Рассмотрим эту опечатку:
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
average = [a, 2*b, 3*c, 4*d, 5*e].smu / total rescue 0
# ^^^
average.round(2)
end
compute_average(1,2,3,4,5)
# => 0.0
compute_average(0,0,0,0,0)
# => 0.0
Рассмотрим использование rescue
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
average.round(2)
rescue ZeroDivisionError
0.0
end
Это лучше, так как оно не скрывает ошибок, но защищает от того же сценария, что и наклон rescue
выше.
Другая версия, с которой я бы назвал обычный средний расчет
В качестве побочного примечания средняя работа, с которой я знаком, вычисляется с использованием total/count, поэтому вот версия, которая делает это.
def compute_average(*values)
# This makes sure arrays get flattened to a single array.
values.flatten!
# Throws away all nil values passed as arguments.
values.reject!(&:nil?)
# Throws away all non-numeric values.
# This includes trashing strings that look like numbers, like "12".
values.keep_if{ |v| v.is_a? Numeric }
total = values.sum.to_f
count = values.count
return Float::NAN if count.zero?
total / count
end
Это защищает от всех случаев:
compute_average(1,2,3,4,5)
=> 3.0
compute_average(0,0,0,0,0)
=> 0.0
compute_average(1,2,nil,4,5)
=> 3.0
compute_average(1,2,"string",4,5)
=> 3.0
compute_average(1)
=> 1.0
compute_average([1,2,3,4,5])
=> 3.0
compute_average
=> NaN
Ответ 7
Я не очень люблю Ruby-ist, но я бы сделал это следующим образом:
average = denominator.nonzero? ? numerator/denominator : 0
Вероятно, есть лучший ответ, но этого может быть достаточно.
Ответ 8
/
не возвращает ошибку с нулевым делением, если либо разделяемое число, либо знаменатель - это float.
def compute_average(a,b,c,d,e)
total = [a,b,c,d,e].sum.to_f
average = [a, 2*b, 3*c, 4*d, 5*e].sum / total
average.finite? ? average.round(2) : 0.0
end
В общем, под ruby1.9,
def compute_average *args
average = args.to_enum.with_index.map{|x, w| x * w}.sum / args.sum.to_f
average.finite? ? average.round(2) : 0.0
end