Как сделать эти две альтернативы о циклическом декартовом произведении менее избыточными? - PullRequest
0 голосов
/ 26 мая 2018

В следующей функции 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)

, который все еще немного избыточен.

Ответы [ 3 ]

0 голосов
/ 26 мая 2018

Если ваша функция слишком повторяющаяся, вы можете пошагово реорганизовать вещи.

Общность, которую вы хотите выделить, - это вызов 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 может просто вернуть 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», bНо если вы хотите, чтобы это распространялось на выбор различных комбинаций трех переменных или произвольных порядков из динамического списка и т. д., это, вероятно, стоило бы сделать.

0 голосов
/ 26 мая 2018

Вы можете отобразить через 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)]
0 голосов
/ 26 мая 2018

Как насчет использования выражения генератора для переворачивания кортежей, когда вы сначала делаете product с источником значений b:

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 раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...