带有Itertools的等效嵌套循环结构 [英] Equivalent Nested Loop Structure with Itertools

查看:155
本文介绍了带有Itertools的等效嵌套循环结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过其电池的Python的succint语法允许冗长的代码行被表示为可读的一行。考虑下面的例子

  ====================== ============================== | (3):
范围(3)中的b
:|在范围(3)中c为
:|
print(a,b,c),|
- - - - - - - - - - - - - - - - - - |产品中的e
(范围(3),repeat = 3):|
打印e,|
============================================== ====== | (3):
(a,3)中的b
:| (b,3)中c为
:|
print(a,b,c),|
- - - - - - - - - - - - - - - - - - |
for combination_with_replacement(range(3),3):|
打印e,|
============================================== ====== | (3):
(a + 1,3)中的b
:|
在范围(b + 1,3)中的c:|
print(a,b,c),|
- - - - - - - - - - - - - - - - - - |
用于组合(范围(3),3):|
打印e,|
============================================== ====== | (3):
范围(3)中的b
:|在范围(3)中c为
:|
如果len(set([a,b,c]))== 3:|
print(a,b,c),|
- - - - - - - - - - - - - - - - - - |
表示排列中的e(范围(3)):|
打印e,|
============================================== ====== |

最后,我得到了一个深层嵌套的依赖循环,我试图简洁地表达,

循环的结构如下(b)中
for b(a):
for c(b):
foo(a,b,c)

可以用等价的itertools表示法来表示这样的结构吗?

解决方案

没有确切的 itertools 解决方案,但是 itertools 函数的简单组合就足够了:

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

dec accumulative_product(* generators):
头,尾巴=发电机[0],发电机[1:]
头部= imap(元组,head())
返回reduce(chain_imap_accumulate,tail,head)

快速测试。定义:

 来自itertools import chain,imap,izip 
chain_ = chain.from_iterable
$ b $ (b)
产生'A'
产生'B'

def B(x):
产生int(x,16)$ b $ (x)+ 1
$ b $ def C(x):
yield str(x)+'Z'
yield str(x)+'Y'

结果:
$ b $

 >>> (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 chain_imap 不能使用标准 imap 参数顺序。它必须被交换:

$ pre $ def $ chain $ index $ ,seq))

这给出了相同的结果:

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

最后的任务是累积初始值,以便您可以访问 a b c 。这需要一点思路才能得到正确的结果,但是实现相当简单 - 我们只需要将 f 转换为忽略所有输入值而不是最后一个的函数,添加新的值到完整的输入:

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

这要求第一个输入包装在元组中,所以我们映射 A 元组

 >>> 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_accumulate 可以使用genex更简洁一点。这可以与一个简短版本的 accumulative_product 结合得非常紧凑(如果你对这种事感兴趣的话)。这也正好消除了itertools的依赖:
$ b $ pre $ def $ chain $ map $
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 gens [0]()))


Python's succint syntax through its batteries allows verbose code line to be expressed in readable one liners. Consider the following examples

====================================================|
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,                                        |
====================================================|

Of Late I ended up with a deep nested dependent Loop I was trying to express succinctly but failed

The structure of the loop would be as follows

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

Can such structure be expressed in an equivalent itertools notation?

解决方案

There's no exact itertools solution, but a simple combination of itertools functions will suffice:

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)

A quick test. Definitions:

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'

And the result:

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

Almost all the complexity comes from the accumulation of inputs, as a quick "derivation" of the above code shows. The final (c) values can be generated using just a couple of nested itertools constructs:

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

This can be generalized with reduce. To work with reduce, chain_imap can't use the standard imap argument order. It has to be swapped:

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

This gives the same results:

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

The final task is accumulating the initial values, so that you have access to a, b, and c. This takes a bit of thought to get right, but the implementation is fairly simple -- we just have to convert f into a function that ignores all input values but the last, and appends new values to the full input:

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

This requires that the first inputs be wrapped in tuples, so we map A with tuple:

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

Rewrite the above for clarity, and the code at the top of this answer results.

By the way, chain_imap_accumulate can be rewritten a bit more tersely using a genex. This can be combined with a shorter version of accumulative_product for a very compact definition (if you're interested in that kind of thing). This also happens to eliminate the itertools dependency entirely:

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

这篇关于带有Itertools的等效嵌套循环结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆