2009-03-04 14 views
3

Ich versuche, eine elegante Methode zum Erstellen einer Liste aus einer Funktion zu erstellen, die Werte in Python und Ruby liefert.Erstellen von Listen mithilfe von Yield in Ruby und Python

In Python:

def foo(x): 
    for i in range(x): 
     if bar(i): yield i 
result = list(foo(100)) 

In Ruby:

def foo(x) 
    x.times {|i| yield i if bar(i)} 
end 
result = [] 
foo(100) {|x| result << x} 

Obwohl ich in beiden Sprachen liebe die Arbeit, ich habe ein immer etwas von der Version Rubin gestört, die die Liste initialisieren und dann fülle es aus. Pythons yield ergibt eine einfache Iteration, was großartig ist. Rubys yield ruft einen Block auf, der auch großartig ist, aber wenn ich nur eine Liste füllen will, fühlt es sich irgendwie klobig an.

Gibt es einen eleganteren Ruby Weg?

UPDATE Das Beispiel wurde überarbeitet, um zu zeigen, dass die Anzahl der Werte, die von der Funktion geliefert werden, nicht unbedingt gleich x ist.

+3

Ruby und Pythons "yield" s sind semantisch unterschiedlich. Die Ausbeute von Python ist fast näher an Rubys Fiber.yield als die Ausbeute von Ruby. – rampion

Antwort

10

Also, für Ihr neues Beispiel, versuchen Sie dies:

def foo(x) 
    (0..x).select { |i| bar(i) } 
end 

Grundsätzlich, wenn Sie einen Iterator der eigenen schreiben, brauchen Sie nicht yield sehr oft in Ruby. Sie werden wahrscheinlich viel besser machen, wenn Sie aufhören, Python-Idiome mit Ruby-Syntax zu schreiben.

+2

Einverstanden. Beide Sprachen haben in vielen Fällen einen völlig anderen Zugang zu gleichwertigen Problemen. – bojo

+2

Sie brauchen nicht einmal den to_a - Bereich enthält Enumerable. Einfach (1..x) .elektieren Sie {| i | bar i}. – Chuck

+1

Ich würde es nicht einmal ein Python-Idiom nennen, siehe unten die Python-Vorschläge. – FogleBird

1

Ich weiß, es ist nicht genau das, was Sie suchen, sondern ein eleganter Weg, um Ihr Beispiel in Ruby auszudrücken ist:

result = Array.new(100) {|x| x*x} 
+0

Sie haben Recht, das ist nicht ganz das, wonach ich suche.Der einzige Grund, warum ich von einer Methode profitieren würde, wäre, wenn ich nicht genau wüsste, wie viele Werte ausgegeben würden. – jcrossley3

1
def squares(x) 
    (0..x).map { |i| i * i } 
end 

Alles, was eine Reihe von Werten, die am besten mit gehandhabt wird, gut , ein Bereich, anstatt times und Array-Generation.

+0

Das Beispiel wurde erfunden. Die Frage bezieht sich auf jede Funktion, die Werte liefert, unabhängig davon, ob ein Bereich betroffen ist oder nicht. – jcrossley3

7

Für die Python-Version ich einen Generator Ausdruck wie verwenden würde:

(i for i in range(x) if bar(i)) 

Oder für diesen speziellen Fall von Filterwerten, noch einfacher

itertools.ifilter(bar,range(x)) 
+0

list comprehensions ftw! –

1

Für die Python Liste Verständnis Version veröffentlicht von Verwenden Sie stbuton xrange anstelle von Bereich, wenn Sie einen Generator möchten. Bereich erstellt die gesamte Liste im Speicher.

+0

Nicht in Python 3.0 .. –

1

yield bedeutet verschiedene Dinge Ruby und Python. In Ruby müssen Sie einen Callback-Block angeben, wenn ich mich richtig erinnere, während Generatoren in Python herumgereicht werden können und denen ergeben, die sie halten.

+0

IMO-Generatoren in Python sind ein Durcheinander. Sie "sehen" wie Funktionen aus, sind aber nicht wie sie - und Sie müssen die Funktion nach dem "Ertrag" suchen, bevor Sie sie als Generatoren und nicht als Funktionen identifizieren können. Und da generator 'functions' generator objects trotzdem zurückgeben ... warum sollte man die API nicht explizit dazu bringen, die generator objects so zu bauen, wie es in Ruby funktioniert? Es scheint, dass Ruby hier expliziter ist als Python - mit Python, der versucht, sie als Funktionen zu tarnen (was völlig die falsche Abstraktion ist). – horseyguy

+0

@banister: Ich hatte eine andere Reaktion auf den Unterschied. Soviel ich über Ruby weiß (nicht sehr weit), kann man ein äquivalentes Konstrukt zu Rubins Generatoren erreichen, indem man einfach einen Funktionsreferenzparameter akzeptiert und ihn nacheinander mit jedem "generierten" Wert aufruft. Dies erfordert nur erstklassige Funktionen, die auch Python besitzt. Der Python-Ansatz verwendet Fortsetzungen, was etwas tiefer gibt. Die magische Syntax um 'yield' sollte für Entwickler nicht zu wichtig sein, da es auch möglich ist, Generatorobjekte aus regulären Funktionen zurückzugeben, wie zB bei Generator-Comprehensions. – recursive

+0

@recursive, ich verstehe nicht ganz, wie das äquivalente Konstrukt in Python funktioniert, könnten Sie ein Beispiel geben? Für die Aufzeichnung Ruby-Generatoren sind mit Fasern implementiert, die vollwertige sprachgestützte Co-Routinen sind. Auch hat Rubin erstklassige Fortsetzungen: P – horseyguy

5

Die genaue Entsprechung Ihres Python-Code (mit Ruby Generatoren) wäre:

def foo(x) 
    Enumerator.new do |yielder| 
     (0..x).each { |v| yielder.yield(v) if bar(v) } 
    end 
end 

result = Array(foo(100)) 

In dem obigen wird die Liste träge generiert (wie im Python Beispiel); siehe:

def bar(v); v % 2 == 0; end 

f = foo(100) 
f.next #=> 0 
f.next #=> 2 
Verwandte Themen