如何使这两个关于循环笛卡尔积的替代方案不那么冗余?
How to make these two alternatives about looping on cartesian product less redundant?
在下面的函数f
中,我们可以先循环a
,也可以先循环b
。
如何让它的代码不那么冗余?
def myfunction(a):
pass
def f(first_loop_on_a=True):
if first_loop_on_a:
for a in range(10):
A = "%010i" % a
myfunction(a)
for b in range(5):
print A, b
else:
for b in range(5):
for a in range(10):
A = "%010i" % a
myfunction(a)
print A, b
f(True)
f(False)
我在考虑 product
,但我们仍然会:
def myfunction2(a, b):
A = "%010i" % a
myfunction(a)
print A, b
def f(first_loop_on_a=True):
if first_loop_on_a:
for a, b in product(range(10), range(5)):
myfunction2(a, b)
else:
for b, a in product(range(5), range(10)):
myfunction2(a, b)
还是有点多余。
当您首先使用 b
值的来源执行 product
时,如何使用生成器表达式来翻转元组:
def f(first_loop_on_a=True):
if first_loop_on_a:
gen = product(range(10, range(5))
else:
gen = (a, b for b, a in product(range(5), range(10)))
for a, b in gen:
myfunction2(a, b)
我会注意到这仍然与您的原始函数不同,因为在原始函数中,myfunction
在两个分支之间被调用的次数不同(10 次或 50 次)。新函数总是在内部循环中调用它(通过 myfunction2
),所以它总是 运行 50 次。
如果你的功能过于重复,你可以一步步重构。
您要排除的共性是在某个迭代器上调用 myfunction2(a, b)
。但是第二个迭代器不仅反转了 product
参数,而且反转了每一对的元素。所以:
def f(first_loop_on_a=True):
if first_loop_on_a:
prod = product(range(10), range(5))
else:
prod = (b, a for (a, b) in product(range(5), range(10)))
for a, b in prod:
myfunction2(a, b)
如果你多次这样做,你可以将元组翻转分解为一个函数:
def flippair(p):
a, b = p
return b, a
def f(first_loop_on_a=True):
if first_loop_on_a:
prod = product(range(10), range(5))
else:
prod = map(flippair, product(range(5), range(10)))
for a, b in prod:
myfunction2(a, b)
(或者,当然,flippair
可以只是 return p[::-1]
——或者,因为你不需要元组,而只是任何一种可迭代的,只需使用 reversed
。但这种方式看起来更明确,而且仍然足够简洁。)
但我认为最好的解决方案是为 myfunction
:
使用关键字参数
def kwify(order, pairs):
return (dict(zip(order, pair)) for pair in pairs)
def f(first_loop_on_a=True):
if first_loop_on_a:
prod = kwify('ab', product(range(10), range(5)))
else:
prod = kwify('ba', product(range(5), range(10)))
for kwpair in prod:
myfunction2(**kwpair)
这很明显,您将 a
值作为 a
传递,将 b
值作为 b
传递,而不是翻转它们,因此它们最终在b
和 a
然后将它们翻转回来以相反的顺序传递它们。
既然如此,为什么要重复这些范围?
def kwify(order, pairs):
return (dict(zip(order, pair)) for pair in pairs)
def f(first_loop_on_a=True):
arange, brange = range(10), range(5)
if first_loop_on_a:
prod = kwify('ab', product(arange, brange))
else:
prod = kwify('ba', product(brange, arange))
for kwpair in prod:
myfunction2(**kwpair)
…此时你也可以给他们起名字:
def kwify(order, pairs):
return (dict(zip(order, pair)) for pair in pairs)
def f(first_loop_on_a=True):
ranges = {'a': range(10), 'b': range(5)}
order = 'ab' if first_loop_on_a else 'ba'
prod = kwify(order, product(*itemgetter(*order)(ranges)))
for kwpair in prod:
myfunction2(**kwpair)
… 或者,甚至可以排除对 range
:
的调用
def kwify(order, pairs):
return (dict(zip(order, pair)) for pair in pairs)
def f(first_loop_on_a=True):
ranges = {'a': 10, 'b': 5}
order = 'ab' if first_loop_on_a else 'ba'
prod = kwify(order, product(*map(range, itemgetter(*order)(ranges))))
for kwpair in prod:
myfunction2(**kwpair)
对于仅选择 "a-then-b" 与 "b-then-a",这可能是可怕的矫枉过正,但如果您希望将其扩展到选择三个变量的不同排列,或动态列表中的任意顺序等., 这可能是值得的。
你可以映射到 reversed
:
>>> import itertools as it
>>>
>>> def itr(A, B, a_first=True):
... return it.product(*map(range, (A, B))) if a_first else map(reversed, it.product(*map(range, (B, A))))
...
>>> [(a, b) for a, b in itr(2, 3, True)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
>>> [(a, b) for a, b in itr(2, 3, False)]
[(0, 0), (1, 0), (0, 1), (1, 1), (0, 2), (1, 2)]
在下面的函数f
中,我们可以先循环a
,也可以先循环b
。
如何让它的代码不那么冗余?
def myfunction(a):
pass
def f(first_loop_on_a=True):
if first_loop_on_a:
for a in range(10):
A = "%010i" % a
myfunction(a)
for b in range(5):
print A, b
else:
for b in range(5):
for a in range(10):
A = "%010i" % a
myfunction(a)
print A, b
f(True)
f(False)
我在考虑 product
,但我们仍然会:
def myfunction2(a, b):
A = "%010i" % a
myfunction(a)
print A, b
def f(first_loop_on_a=True):
if first_loop_on_a:
for a, b in product(range(10), range(5)):
myfunction2(a, b)
else:
for b, a in product(range(5), range(10)):
myfunction2(a, b)
还是有点多余。
当您首先使用 b
值的来源执行 product
时,如何使用生成器表达式来翻转元组:
def f(first_loop_on_a=True):
if first_loop_on_a:
gen = product(range(10, range(5))
else:
gen = (a, b for b, a in product(range(5), range(10)))
for a, b in gen:
myfunction2(a, b)
我会注意到这仍然与您的原始函数不同,因为在原始函数中,myfunction
在两个分支之间被调用的次数不同(10 次或 50 次)。新函数总是在内部循环中调用它(通过 myfunction2
),所以它总是 运行 50 次。
如果你的功能过于重复,你可以一步步重构。
您要排除的共性是在某个迭代器上调用 myfunction2(a, b)
。但是第二个迭代器不仅反转了 product
参数,而且反转了每一对的元素。所以:
def f(first_loop_on_a=True):
if first_loop_on_a:
prod = product(range(10), range(5))
else:
prod = (b, a for (a, b) in product(range(5), range(10)))
for a, b in prod:
myfunction2(a, b)
如果你多次这样做,你可以将元组翻转分解为一个函数:
def flippair(p):
a, b = p
return b, a
def f(first_loop_on_a=True):
if first_loop_on_a:
prod = product(range(10), range(5))
else:
prod = map(flippair, product(range(5), range(10)))
for a, b in prod:
myfunction2(a, b)
(或者,当然,flippair
可以只是 return p[::-1]
——或者,因为你不需要元组,而只是任何一种可迭代的,只需使用 reversed
。但这种方式看起来更明确,而且仍然足够简洁。)
但我认为最好的解决方案是为 myfunction
:
def kwify(order, pairs):
return (dict(zip(order, pair)) for pair in pairs)
def f(first_loop_on_a=True):
if first_loop_on_a:
prod = kwify('ab', product(range(10), range(5)))
else:
prod = kwify('ba', product(range(5), range(10)))
for kwpair in prod:
myfunction2(**kwpair)
这很明显,您将 a
值作为 a
传递,将 b
值作为 b
传递,而不是翻转它们,因此它们最终在b
和 a
然后将它们翻转回来以相反的顺序传递它们。
既然如此,为什么要重复这些范围?
def kwify(order, pairs):
return (dict(zip(order, pair)) for pair in pairs)
def f(first_loop_on_a=True):
arange, brange = range(10), range(5)
if first_loop_on_a:
prod = kwify('ab', product(arange, brange))
else:
prod = kwify('ba', product(brange, arange))
for kwpair in prod:
myfunction2(**kwpair)
…此时你也可以给他们起名字:
def kwify(order, pairs):
return (dict(zip(order, pair)) for pair in pairs)
def f(first_loop_on_a=True):
ranges = {'a': range(10), 'b': range(5)}
order = 'ab' if first_loop_on_a else 'ba'
prod = kwify(order, product(*itemgetter(*order)(ranges)))
for kwpair in prod:
myfunction2(**kwpair)
… 或者,甚至可以排除对 range
:
def kwify(order, pairs):
return (dict(zip(order, pair)) for pair in pairs)
def f(first_loop_on_a=True):
ranges = {'a': 10, 'b': 5}
order = 'ab' if first_loop_on_a else 'ba'
prod = kwify(order, product(*map(range, itemgetter(*order)(ranges))))
for kwpair in prod:
myfunction2(**kwpair)
对于仅选择 "a-then-b" 与 "b-then-a",这可能是可怕的矫枉过正,但如果您希望将其扩展到选择三个变量的不同排列,或动态列表中的任意顺序等., 这可能是值得的。
你可以映射到 reversed
:
>>> import itertools as it
>>>
>>> def itr(A, B, a_first=True):
... return it.product(*map(range, (A, B))) if a_first else map(reversed, it.product(*map(range, (B, A))))
...
>>> [(a, b) for a, b in itr(2, 3, True)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
>>> [(a, b) for a, b in itr(2, 3, False)]
[(0, 0), (1, 0), (0, 1), (1, 1), (0, 2), (1, 2)]