13

バッテリを使用した Python の簡潔な構文により、冗長なコード行を読みやすい 1 つのライナーで表現できます。次の例を検討してください

====================================================|
for a in range(3):                                  |
    for b in range(3):                              |
        for c in range(3):                          |
            print (a,b,c),                          |
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -|
for e in product(range(3), repeat=3):               |
    print e,                                        |
====================================================|
for a in range(3):                                  |
    for b in range(a , 3):                          |
        for c in range(b , 3):                      |
            print (a,b,c),                          |
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -|
for e in combinations_with_replacement(range(3), 3):|
    print e,                                        |
====================================================|
for a in range(3):                                  |
    for b in range(a + 1, 3):                       |
        for c in range(b + 1, 3):                   |
            print (a,b,c),                          |
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -|
for e in combinations(range(3), 3):                 |
    print e,                                        |
====================================================|
for a in range(3):                                  |
    for b in range(3):                              |
        for c in range(3):                          |
            if len(set([a,b,c])) == 3:              |
                print (a,b,c),                      |
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -|
for e in permutations(range(3)):                    |
    print e,                                        |
====================================================|

最近、深くネストされた依存ループになってしまいました簡潔に表現しようとしましたが失敗しました

ループの構造は次のようになります

for a in A():
    for b in B(a):
        for c in C(b):
            foo(a,b,c)

そのような構造は、同等の itertools 表記で表現できますか?

4

2 に答える 2

5

正確なitertools解決策はありませんが、関数の単純な組み合わせでitertools十分です。

def chain_imap_accumulate(seq, f):
    def acc_f(x):
        for n in f(x[-1]):
            yield x + (n,)
    return chain.from_iterable(imap(acc_f, seq))

def accumulative_product(*generators):
    head, tail = generators[0], generators[1:]
    head = imap(tuple, head())
    return reduce(chain_imap_accumulate, tail, head)

簡単なテスト。定義:

from itertools import chain, imap, izip
chain_ = chain.from_iterable

def A():
    yield 'A'
    yield 'B'

def B(x):
    yield int(x, 16)
    yield int(x, 16) + 1

def C(x):
    yield str(x) + 'Z'
    yield str(x) + 'Y'

そして結果:

>>> list(accumulative_product(A, B, C))
[('A', 10, '10Z'), ('A', 10, '10Y'), 
 ('A', 11, '11Z'), ('A', 11, '11Y'), 
 ('B', 11, '11Z'), ('B', 11, '11Y'), 
 ('B', 12, '12Z'), ('B', 12, '12Y')]

上記のコードの簡単な「派生」が示すように、ほとんどすべての複雑さは入力の蓄積から生じます。最終的な( ) 値は、いくつかのネストされた構造cを使用して生成できます。itertools

>>> list(chain_(imap(C, chain_(imap(B, (A()))))))
['10Z', '10Y', '11Z', '11Y', '11Z', '11Y', '12Z', '12Y']

これは で一般化できますreduce。を使用するreducechain_imapは、標準のimap引数の順序を使用できません。交換する必要があります:

def chain_imap(seq, f):
    return chain.from_iterable(imap(f, seq))

これにより、同じ結果が得られます。

>>> list(reduce(chain_imap, [B, C], A()))
['10Z', '10Y', '11Z', '11Y', '11Z', '11Y', '12Z', '12Y']

a最後のタスクは、初期値を累積して、 、b、および にアクセスできるようにすることですc。これを正しく行うには少し考えが必要ですが、実装はかなり単純fです。最後の入力値を除くすべての入力値を無視し、完全な入力に新しい値を追加する関数に変換するだけです。

def chain_imap_accumulate(seq, f):
    def acc_f(x):
        for n in f(x[-1]):
            yield x + (n,)
    return chain.from_iterable(imap(acc_f, seq))

これには、最初の入力をタプルでラップする必要があるため、次のようにマップAtupleます。

>>> list(reduce(chain_imap_accumulate, [B, C], imap(tuple, A())))
[('A', 10, '10Z'), ('A', 10, '10Y'), 
 ('A', 11, '11Z'), ('A', 11, '11Y'), 
 ('B', 11, '11Z'), ('B', 11, '11Y'), 
 ('B', 12, '12Z'), ('B', 12, '12Y')]

わかりやすくするために上記を書き直すと、この回答の上部にあるコードが得られます。

ちなみに、chain_imap_accumulategenex を使用すると、もう少し簡潔に書き直すことができます。これは、短いバージョンの と組み合わせてaccumulative_product非常にコンパクトな定義にすることができます (そのようなことに興味がある場合)。これにより、itertools の依存関係が完全に解消されます。

def chain_map_accumulate(seq, f):
    return (x + (n,) for x in seq for n in f(x[-1]))

def accumulative_product2(*gens):
    return reduce(chain_map_accumulate, gens[1:], (tuple(x) for x in gens[0]()))
于 2013-02-23T16:22:13.430 に答える
4

ありませんが、作成できます。

def chainGang(steps, currentVars=None):
    thisOne = steps[0]
    if currentVars is None:
        for item in thisOne():
            for gang in chainGang(steps[1:], [item]):
                yield gang
    elif len(steps) == 1:       
        for item in thisOne(currentVars[-1]):
            yield currentVars + [item]
    else:
        for item in thisOne(currentVars[-1]):
            for gang in chainGang(steps[1:], currentVars + [item]):
                yield gang

その後:

>>> outer = lambda: ["A", "B", "C", "D"]
>>> middle = lambda letter: [letter, letter*2, letter*3]
>>> inner = lambda s: range(len(s)+1)
>>> for a in chainGang([outer, middle, inner]):
...     print a
[u'A', u'A', 0]
[u'A', u'A', 1]
[u'A', u'AA', 0]
[u'A', u'AA', 1]
[u'A', u'AA', 2]
[u'A', u'AAA', 0]
[u'A', u'AAA', 1]
[u'A', u'AAA', 2]
[u'A', u'AAA', 3]
[u'B', u'B', 0]
[u'B', u'B', 1]
[u'B', u'BB', 0]
[u'B', u'BB', 1]
[u'B', u'BB', 2]
[u'B', u'BBB', 0]
[u'B', u'BBB', 1]
[u'B', u'BBB', 2]
[u'B', u'BBB', 3]
[u'C', u'C', 0]
[u'C', u'C', 1]
[u'C', u'CC', 0]
[u'C', u'CC', 1]
[u'C', u'CC', 2]
[u'C', u'CCC', 0]
[u'C', u'CCC', 1]
[u'C', u'CCC', 2]
[u'C', u'CCC', 3]
[u'D', u'D', 0]
[u'D', u'D', 1]
[u'D', u'DD', 0]
[u'D', u'DD', 1]
[u'D', u'DD', 2]
[u'D', u'DDD', 0]
[u'D', u'DDD', 1]
[u'D', u'DDD', 2]
[u'D', u'DDD', 3]
于 2013-02-23T05:44:28.867 に答える