再帰的手続き内の「yield」


8

いくつかの変数の範囲を表すPythonリストがあるとします。

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

これは、変数のi範囲が1〜5で、ループ変数のj範囲が1〜2であることを表しています。可能な組み合わせごとに辞書が必要です。

{'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}

その理由は、それらを繰り返し処理したいからです。しかし、スペース全体が大きすぎるため、それらをすべて生成して保存し、辞書のリストを反復処理することはしたくありません。次の再帰的な手順を使用することを考えましたが、そのyield部分についていくつかの助けが必要です。どこにあるの?ネストされたジェネレーターを回避するにはどうすればよいですか?

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)

今私はできるようになりたいと思います:

for valued_indices in iteration(conditions, 0, {}):
    ...


3
単純に置き換えるyieldyield from、あなたの関数の非常に最後の行に。
jfaccioni

回答:


5

これは、一歩戻って最初からやり直す方が簡単な場合です。

まず、よく知られているトリックを使用して、キーと間隔を個別に取得しますzip

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

(2つの間の対応は元のペアリングを保持します。すべてintervals[i]の変数の間隔です。)keys[i]i

次に、これらの間隔から適切な範囲オブジェクトを作成しましょう

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

これらのrangeオブジェクトの積を計算できます

>>> 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)

これを、各辞書に使用する値として認識してください。これらの各値をキーで圧縮して、dictコマンドの適切な引数のセットを作成できます。例えば:

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

これをすべてまとめると、製品を反復処理してそれぞれdictを順番に生成し、それを生成できます。

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))

0

あなたはおそらく内部ジェネレーターの理解とyield fromそれで少し単純化することができます:

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

使用:

foo = dict_factory(5, 2)
while True:
    print(next(foo))

与えられた条件はリストのリストとして渡されるので、OPは2にハードコーディングされていないN個の条件に対して機能するソリューションが必要だと思います
Chris Doyle

@Chris Doyle良い点、関数はおそらくそのように構造化されるべきdict_factory(*args)です...うーん...
neutrino_logic

0

特に再帰を使用する必要があるかどうかはわかりませんが、itertools製品のメソッドを使用してこれらを生成し、すべての組み合わせを生成できます。これは、1からnの条件を持つことができるように記述されているため、機能し、アイテムごとに返されます。

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)

出力

{'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}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.