"Ausbeute" innerhalb einer rekursiven Prozedur

8

Angenommen, ich habe eine Python-Liste, die Bereiche für einige Variablen darstellt:

conditions = [['i', (1, 5)], ['j', (1, 2)]]

Dies stellt dar, dass die Variable izwischen 1 und 5 liegt und innerhalb dieser Schleife jzwischen 1 und 2 liegt. Ich möchte ein Wörterbuch für jede mögliche Kombination:

{'i': 1, 'j': 1}
{'i': 1, 'j': 2}
{'i': 2, 'j': 1}
{'i': 2, 'j': 2}
{'i': 3, 'j': 1}
{'i': 3, 'j': 2}
{'i': 4, 'j': 1}
{'i': 4, 'j': 2}
{'i': 5, 'j': 1}
{'i': 5, 'j': 2}

Der Grund ist, dass ich über sie iterieren möchte. Aber weil der gesamte Speicherplatz zu groß ist, möchte ich nicht alle generieren, speichern und dann diese Liste von Wörterbüchern durchlaufen. Ich habe überlegt, das folgende rekursive Verfahren zu verwenden, aber ich brauche Hilfe bei dem yieldTeil. Wo soll es sein Wie vermeide ich verschachtelte Generatoren?

def iteration(conditions, currentCondition, valuedIndices):
    if currentCondition == len(conditions):
        yield valuedIndices
    else:
        cond = conditions[currentCondition]
        index = cond[0]
        lim1 = cond[1][0]
        lim2 = cond[1][1]
        for ix in range(lim1, lim2 + 1):
            valuedIndices[index] = ix
            yield iteration(conditions, currentCondition + 1, valuedIndices)

Jetzt möchte ich in der Lage sein:

for valued_indices in iteration(conditions, 0, {}):
    ...
Noel Arteche
quelle
3
Ersetzen Sie einfach yieldmit yield fromin der allerletzten Zeile Ihrer Funktion.
jfaccioni

Antworten:

5

Dies ist ein Fall, in dem es möglicherweise einfacher ist, einen Schritt zurückzutreten und neu zu beginnen.

Beginnen wir damit, die Schlüssel und die Intervalle mit einem bekannten Trick zu trennen zip:

>>> keys, intervals = list(zip(*conditions))
>>> keys
('i', 'j')
>>> intervals
((1, 5), (1, 2))

(Die Entsprechung zwischen den beiden behält die ursprüngliche Paarung bei; intervals[i]ist das Intervall für die Variable keys[i]für alle i.)

Lassen Sie uns nun aus diesen Intervallen geeignete Bereichsobjekte erstellen

>>> intervals = [range(x, y+1) for x, y in intervals]
>>> list(intervals[0])
[1, 2, 3, 4, 5]
>>> list(intervals[1])
[1, 2]

Wir können das Produkt dieser rangeObjekte berechnen

>>> for v in product(*intervals):
...   print(v)
...
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
(4, 1)
(4, 2)
(5, 1)
(5, 2)

die Sie als die Werte erkennen sollten, die für jedes Diktat verwendet werden sollen. Sie können jeden dieser Werte mit den Schlüsseln komprimieren, um einen geeigneten Satz von Argumenten für den dictBefehl zu erstellen . Zum Beispiel:

>>> dict(zip(keys, (1,1)))
{'i': 1, 'j': 1}

Wenn wir dies alles zusammenfassen, können wir das Produkt durchlaufen, um es nacheinander zu produzieren dictund es zu erhalten.

def iteration(conditions):
    keys, intervals = zip(*conditions)
    intervals = [range(x, y+1) for x, y in intervals]
    yield from (dict(zip(keys, v)) for v in product(*intervals))
chepner
quelle
0

Sie können vielleicht ein wenig mit einem internen Generatorverständnis vereinfachen und yield fromes:

def dict_factory(i, j):
    r1 = range(1, i + 1)
    r2 = range(1, j + 1)
    dictgen = ({'i':x, 'j':y} for x in r1 for y in r2)
    yield from dictgen

benutzen als:

foo = dict_factory(5, 2)
while True:
    print(next(foo))
neutralino_logic
quelle
Die gegebenen Bedingungen werden als Liste von Listen übergeben. Ich vermute, dass das OP eine Lösung benötigt, die für N Anzahl von Bedingungen funktioniert, die nicht fest auf 2 codiert sind
Chris Doyle
@ Chris Doyle guter Punkt, die Funktion sollte wahrscheinlich so strukturiert sein wie dict_factory(*args)damals ... Hmmm ...
neutrino_logic
0

Ich bin nicht sicher, ob Sie speziell die Rekursion verwenden müssen oder nicht, aber Sie könnten diese mithilfe der itertools-Produktmethode generieren, um alle Kombinationen zu generieren. Dies ist so geschrieben, dass Sie 1 bis n Bedingungen haben können und es weiterhin funktionieren und Sie Artikel für Artikel zurücksenden sollte.

from itertools import product


def iterations2(conditions):
    labels, ranges = list(zip(*conditions))
    ranges = [range(item[0], item[1] + 1) for item in ranges]
    for nums in product(*ranges):
        yield dict(zip(labels, nums))


conditions = [['i', (1, 5)], ['j', (1, 2)], ['z', (3, 6)]]
for valued_indices in iterations2(conditions):
    print(valued_indices)

AUSGABE

{'i': 1, 'j': 1, 'z': 3}
{'i': 1, 'j': 1, 'z': 4}
{'i': 1, 'j': 1, 'z': 5}
{'i': 1, 'j': 1, 'z': 6}
{'i': 1, 'j': 2, 'z': 3}
{'i': 1, 'j': 2, 'z': 4}
{'i': 1, 'j': 2, 'z': 5}
{'i': 1, 'j': 2, 'z': 6}
{'i': 2, 'j': 1, 'z': 3}
{'i': 2, 'j': 1, 'z': 4}
{'i': 2, 'j': 1, 'z': 5}
{'i': 2, 'j': 1, 'z': 6}
{'i': 2, 'j': 2, 'z': 3}
{'i': 2, 'j': 2, 'z': 4}
{'i': 2, 'j': 2, 'z': 5}
{'i': 2, 'j': 2, 'z': 6}
{'i': 3, 'j': 1, 'z': 3}
{'i': 3, 'j': 1, 'z': 4}
{'i': 3, 'j': 1, 'z': 5}
{'i': 3, 'j': 1, 'z': 6}
{'i': 3, 'j': 2, 'z': 3}
{'i': 3, 'j': 2, 'z': 4}
{'i': 3, 'j': 2, 'z': 5}
{'i': 3, 'j': 2, 'z': 6}
{'i': 4, 'j': 1, 'z': 3}
{'i': 4, 'j': 1, 'z': 4}
{'i': 4, 'j': 1, 'z': 5}
{'i': 4, 'j': 1, 'z': 6}
{'i': 4, 'j': 2, 'z': 3}
{'i': 4, 'j': 2, 'z': 4}
{'i': 4, 'j': 2, 'z': 5}
{'i': 4, 'j': 2, 'z': 6}
{'i': 5, 'j': 1, 'z': 3}
{'i': 5, 'j': 1, 'z': 4}
{'i': 5, 'j': 1, 'z': 5}
{'i': 5, 'j': 1, 'z': 6}
{'i': 5, 'j': 2, 'z': 3}
{'i': 5, 'j': 2, 'z': 4}
{'i': 5, 'j': 2, 'z': 5}
{'i': 5, 'j': 2, 'z': 6}
Chris Doyle
quelle