2012-05-23 5 views
11

die erste Projekt Euler Frage zu tun: die Vielfachen von 3 und 5 zwischen 1 und 1000 Summieren, kam ich mit diesem nach oben (sehr einfach)Rubin injizieren mit bedingten in Block?

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

aber ich dachte, dies funktionieren würde, aber es funktioniert nicht, kann jemand zeigt mir, was ich falsch mache, oder warum es nicht funktioniert?

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

danke!

Antwort

22

inject übergibt das Ergebnis des Blocks an die nächste Iteration als erstes Argument. Ihr Block wird nil zurückgeben, wenn Ihre if-Anweisung falsch ist, die dann als sum zurückgegeben wird.

Um die richtige Antwort zu erhalten, sollte der Block die aktuelle Summe zurück, wenn es falsch ist:

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 } 

auch (die += beachten) funktionieren würde.

+0

danke für die Antwort alt. Dies ist näher an meiner ursprünglichen Einstellung und so würde ich es wahrscheinlich (lesbarer für mich) tun, ich akzeptierte die erste Antwort wegen der Erklärung des Fehlers - der 'Aha!' Moment zu mir geholfen – Tonys

3

Komplementäre Antwort: Wenn Sie Euler-Probleme lösen wollen, sollten Sie damit beginnen, eigene Erweiterungen von wiederverwendbarem Code zu erstellen. In diesem Fall wäre die erste Erweiterung Enumerable#sum sein:

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

Und jetzt können Sie eine Lösung schreiben, die den Zustand des summatory trennt (man kann es lesen laut und es macht Sinn, das ist typisch für funktionale/deklarative Stil):

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

Mehr:

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

Sie es sogar noch einen Schritt weiter drücken könnte und erstellen Fixnum#divisible_by? so können Sie schreiben hier es ist kein Problem, aber später strenge Implementierungen (diejenigen, die Arrays verwenden) werden zu viel Speicher benötigen. Versuchen Sie dann mit laziness:

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

Das ist eine gute Info. Ich werde es als ein paar Fragen in folgen und ich sehe die Nützlichkeit des Rates. Vielen Dank! – Tonys

2

Oder verwenden & proc, die sich selbst richtet.

(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(:+) der Vollständigkeit halber.