Рубин впрыскивать условно в блок?
выполнив первый проект Эйлера вопрос: суммируя кратные 3 и 5 между 1 и 1000, я придумал это (довольно просто)
sum = 0
1.upto(999) { |i| sum += i if 0 == i%3 || 0 == i%5 }
sum
но я думал, что это сработает, но это не так, может кто-нибудь показать мне, что я делаю неправильно, или почему это не работает?
1.upto(999).inject(0) { |sum, i| sum + i if 0 == i%3 || 0 == i%5 }
спасибо!
Ответы
Ответ 1
inject
передает результат от блока до следующей итерации в качестве первого аргумента. Ваш блок вернет nil
, когда ваш оператор if
будет ложным, а затем будет передан обратно как sum
.
Чтобы получить правильный ответ, блок должен вернуть текущую сумму, когда она ложна:
1.upto(999).inject(0) { |sum, i| (0 == i%3 || 0 == i%5) ? sum + i : sum }
Ответ 2
Дополнительный ответ: если вы собираетесь решать проблемы Эйлера, вы должны начать создавать свои собственные расширения кода многократного использования. В этом случае первое расширение будет Enumerable#sum
:
module Enumerable
def sum
inject(0, :+)
end
end
И теперь вы можете написать решение, разделяющее условие суммирования (вы можете прочитать его вслух, и это имеет смысл, типичный для функционального/декларативного стиля):
1.upto(999).select { |x| x % 3 == 0 || x % 5 == 0 }.sum
Вы даже можете нажать на один шаг дальше и создать Fixnum#divisible_by?
, чтобы вы могли написать:
1.upto(999).select { |x| x.divisible_by?(3) || x.divisible_by?(5) }.sum
Дополнительно: здесь это не проблема, но позже строгие реализации (те, которые используют массивы) потребуют слишком много памяти. Попробуйте затем лень:
require 'lazy'
1.upto(999).lazy.select { |x| x % 3 == 0 || x % 5 == 0 }.sum
Ответ 3
1.upto(999).inject(0) { |sum, i| sum += i if 0 == i%3 || 0 == i%5; sum }
Также будет работать (обратите внимание на +=
).
Ответ 4
Или используйте &
proc, который обращается к себе.
(1..999).select{|x| x%3==0||x%5==0}.inject &:+
Ответ 5
(1..999).to_a.keep_if{|d| d%3 == 0 || d%5 == 0}.reduce(:+)
для полноты.