2017-11-07 4 views
-1

Ruby hat viele nette Möglichkeiten, das Ergebnis zu wiederholen und direkt zurückzusenden. Dies betrifft hauptsächlich Array-Methoden. Zum Beispiel:Was ist der sauberste Weg, um ein Ruby-Array mit einer While-Schleife zu erstellen?

def ten_times_tables 
    (1..5).map { |i| i * 10 } 
end 

ten_times_tables # => [10, 20, 30, 40, 50] 

Allerdings mag ich manchmal iterieren while verwenden und direkt die resultierenden Array zurück. Zum Beispiel kann der Inhalt des Arrays vom erwarteten Endwert oder einem Akkumulator oder sogar von Bedingungen außerhalb unserer Kontrolle abhängen.

A (erfundene) Beispiel könnte wie folgt aussehen:

def fibonacci_up_to(max_number) 
    sequence = [1, 1] 

    while sequence.last < max_number 
    sequence << sequence[-2..-1].reduce(:+) 
    end 

    sequence 
end 

fibonacci_up_to(5) # => [1, 1, 2, 3, 5] 

Für mich ist diese Art von Ansatz fühlt sich ganz "un-Ruby". Die Tatsache, dass ich ein Array konstruiere, nenne und später zurückgebe, fühlt sich an wie ein Anti-Pattern. Bisher ist die beste, die ich mit oben kommen kann verwendet tap, aber es fühlt sich immer noch ziemlich eklig (und ziemlich verschachtelt):

def fibonacci_up_to(max_number) 
    [1, 1].tap do |sequence| 
    while sequence.last < max_number 
     sequence << sequence[-2..-1].reduce(:+) 
    end 
    end 
end 

Hat jemand irgendwelche cleveren Lösungen für diese Art von Problem?

+0

Ihr erfundenes Szenario ist, wie Sie sagen, ziemlich ungewöhnlich. Daher sieht der Code zum Erreichen des Ergebnisses auch etwas ungewöhnlich aus. Aber ich sehe nichts besonders falsch mit Ihrer ursprünglichen Herangehensweise oder irgendeine signifikante Möglichkeit, sie zu verbessern. –

Antwort

5

Etwas, das man vielleicht in für Situationen wie diese aussehen soll (wenn auch vielleicht Ihr konstruiertes Beispiel paßt dies viel besser als Ihr tatsächlicher Anwendungsfall) ein Enumerator erstellen, so dass Ihr konstruiertes Beispiel wird:

Aus der docs für initialize:

fib = Enumerator.new do |y| 
    a = b = 1 
    loop do 
    y << a 
    a, b = b, a + b 
    end 
end 

und dann rufen:

p fib.take_while { |elem| elem <= 5 } 
#=> [1, 1, 2, 3, 5] 

Also, Sie einen Enumerator erstellen, die alle Ihre Werte iteriert und dann, wenn Sie das haben, können Sie durch sie durchlaufen und sammeln Sie die Werte, die Sie in einer der üblichen Rubin-ish Möglichkeiten für Ihr Array wollen

0

Ähnlich einfache Lime Enumerator Lösung, können Sie eine Methode schreiben, die sich in einem Enumerator wickelt:

def fibonacci_up_to(max_number) 
    return enum_for(__callee__, max_number) unless block_given? 

    a = b = 1 
    while a <= max_number 
    yield a 
    a, b = b, a + b 
    end 
end 

fibonacci_up_to(5).to_a # => [1, 1, 2, 3, 5] 

Dies erreicht das gleiche Ergebnis wie ein Enumerator-Instanz von einer Methode zurückkehrt, aber es sieht ein bisschen schöner und Sie können die yield verwenden Schlüsselwort anstelle einer yielder Blockvariable. Sie können auch nette Dinge wie:

fibonacci_up_to(5) do |i| 
    # .. 
end 
Verwandte Themen