2012-05-23 15 views
11

hacer la primera pregunta Proyecto Euler: sumando los múltiplos de 3 y 5 entre 1 y 1000, se me ocurrió esto (bastante simple)ruby ​​inject with conditional in block?

sum = 0 
1.upto(999) { |i| sum += i if 0 == i%3 || 0 == i%5 } 
sum 

pero pensé que esto iba a funcionar pero no es así, puedo ¿alguien me muestra lo que estoy haciendo mal, o por qué no funciona?

1.upto(999).inject(0) { |sum, i| sum + i if 0 == i%3 || 0 == i%5 } 

thanks!

Respuesta

22

inject pasa el resultado del bloque a la siguiente iteración como primer argumento. Su bloque devolverá nil cuando su declaración if es falsa, que luego se vuelve a pasar como sum.

para obtener la respuesta correcta, el bloque debe devolver la suma actual cuando es falsa:

1.upto(999).inject(0) { |sum, i| (0 == i%3 || 0 == i%5) ? sum + i : sum } 
2
1.upto(999).inject(0) { |sum, i| sum += i if 0 == i%3 || 0 == i%5; sum } 

también funcionaría (nótese el +=).

+0

gracias por la respuesta alt. Esto está más cerca de mi configuración inicial, por lo que probablemente lo haga de esta manera (más legible para mí), acepté la primera respuesta debido a la explicación del error: el '¡Ajá!' momento para mí ayudó – Tonys

3

Respuesta complementaria: si está a punto de enfrentarse a los problemas de Euler, debería comenzar a crear sus propias extensiones de código reutilizable. En este caso, la primera extensión sería Enumerable#sum:

module Enumerable 
    def sum 
    inject(0, :+) 
    end 
end 

y ahora se puede escribir una solución que separa el estado de la sumatoria (se puede leer en voz alta y tiene sentido, eso es típico de funcional/declarativa estilo):

1.upto(999).select { |x| x % 3 == 0 || x % 5 == 0 }.sum 

incluso se puede empujar un paso más allá y crear Fixnum#divisible_by? para que pueda escribir:

1.upto(999).select { |x| x.divisible_by?(3) || x.divisible_by?(5) }.sum 

Más: aquí es no es un problema, pero más tarde las implementaciones estrictas (las que usan matrices) requerirán demasiada memoria. Intenta luego con laziness:

require 'lazy' 
1.upto(999).lazy.select { |x| x % 3 == 0 || x % 5 == 0 }.sum 
+0

Esta es una gran información. Voy a seguirlo como unas pocas preguntas y veo la utilidad del consejo. ¡Gracias! – Tonys

2

O bien, utilizar & proc que se ocupa de sí mismo.

(1..999).select{|x| x%3==0||x%5==0}.inject &:+ 
1

(1..999).to_a.keep_if{|d| d%3 == 0 || d%5 == 0}.reduce(:+) para completar.