Рубин впрыскивать условно в блок?

выполнив первый проект Эйлера вопрос: суммируя кратные 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(:+) для полноты.