2017-07-13 4 views
7

Betrachten Sie die folgende Funktion, deren Ausgang soll das Kreuzprodukt aus einer Folge von Iterables sein:Warum funktioniert meine kartesische Produktfunktion nicht?

def cart(*iterables): 
    out = ((e,) for e in iterables[0]) 
    for iterable in iterables[1:]: 
     out = (e1 + (e2,) for e1 in out for e2 in iterable) 
    return out 

feine Arbeiten, wenn Generator Comprehensions mit Listenkomprehensionen ersetzt werden. Funktioniert auch, wenn nur 2 Iterables vorhanden sind. Aber wenn ich versuche

print(list(cart([1, 2, 3], 'ab', [4, 5]))) 

ich

[(1, 4, 4), (1, 4, 5), (1, 5, 4), (1, 5, 5), 
(2, 4, 4), (2, 4, 5), (2, 5, 4), (2, 5, 5), 
(3, 4, 4), (3, 4, 5), (3, 5, 4), (3, 5, 5)] 

Warum dies und nicht das kartesische Produkt?

+0

Sie könnten Zwischenergebnisse im Speicher speichern (wie der Listenansatz, der funktioniert) und ihre Auswertung nicht mit diesem Gen verzögern. exp. deren Werte sich wiederholt über Iterationen hinweg ändern. –

+1

Ich weiß, dass es bei dieser Frage darum geht, den Algorithmus für kartesische Produkte in Python zu implementieren, aber für den Fall, dass jemand hier nach einem kartesischen Produkt in Python sucht, ist dies bereits in ['itertools.product'] implementiert (https://docs.python.org/3/library/itertools.html#itertools.product). – jdehesa

Antwort

8

Sie erstellen Generatorausdrücke, die nicht bis zur nächsten Iteration der for iterable in iterables[1:]:-Schleife durchlaufen werden. Sie verwenden Verschlüsse, die zur Laufzeit nachgeschlagen werden.

Generatorausdrücke sind im Wesentlichen kleine Funktionen in dieser Hinsicht, sie erstellen ihren eigenen Bereich, und alle Namen aus dem übergeordneten Bereich müssen als Schließungen behandelt werden, damit dies funktioniert. Die 'Funktion' wird ausgeführt, wenn Sie iterieren, und nur dann wird die Schließung benötigt und in den aktuellen Wert der referenzierten Variable aufgelöst.

So erstellen Sie einen Generator Ausdruck wie folgt aus:

(e1 + (e2,) for e1 in out for e2 in iterable) 

wo iterable ein Verschluss von dem übergeordneten Bereich (Ihre Funktion Einheimischen) aufgenommen ist. Die Suche wird jedoch erst bei der nächsten Iteration durchgeführt, wenn Sie die Schleife ausführen, wobei iterable das nächste Element in der Sequenz ist.

Also für Ihre Eingabe von [1, 2, 3], 'ab', [4, 5] erstellen Sie einen Generator Ausdruck, wenn iterable = 'ab' sondern durch die Zeit, die Sie tatsächlich durchlaufen, die for Schleife einen neuen Wert zugewiesen hat und nun iterable = [4, 5]. Wenn Sie schließlich über den letzten (verketteten) Generator iterieren, zählt nur die allerletzte Zuweisung zu iterable.

Sie erstellen effektiv ein Produkt über iterables[0], iterables[-1] * len(iterables) - 1; iterables[1] bis iterables[-2] werden komplett übersprungen, alle ersetzt durch iterables[-1].

Sie einen Generator Funktion nutzen könnte die Schließung Problem zu vermeiden, in iterable vorbei an einem lokalen gebunden zu sein:

def gen_step(out, iterable): 
    for e1 in out: 
     for e2 in iterable: 
      yield e1 + (e2,) 

def cart(*iterables): 
    out = ((e,) for e in iterables[0]) 
    for iterable in iterables[1:]: 
     out = gen_step(out, iterable) 
    return out 

Sie das gleiche mit einem Lambda tun könnten den Generator Ausdruck Rückkehr:

def cart(*iterables): 
    out = ((e,) for e in iterables[0]) 
    for iterable in iterables[1:]: 
     out = (lambda it=iterable: (e1 + (e2,) for e1 in out for e2 in it))() 
    return out 
+0

Die Alternativen sind immer noch faul. Nett. –

Verwandte Themen