Python начало генераторов - PullRequest
       11

Python начало генераторов

0 голосов
/ 28 сентября 2018

Я пытаюсь найти общий способ генерации всех комбинаций нескольких диапазонов или списков, например,

[range(0, 2), range(2, 5), range(4, 6), range(2, 3)],

, который должен возвращать элемент 2x3x2x1 = 12list.

[[0, 2, 4, 2],
 [0, 2, 5, 2],
 [0, 3, 4, 2],
 [0, 3, 5, 2],
 [0, 4, 4, 2],
 [0, 4, 5, 2],
 [1, 2, 4, 2],
 [1, 2, 5, 2],
 [1, 3, 4, 2],
 [1, 3, 5, 2],
 [1, 4, 4, 2],
 [1, 4, 5, 2]]

Пока все хорошо.Когда я жестко его кодирую, выполняя

x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
x.append( ( a + [b] for a in x[-1] for b in rgs[2]) )
x.append( ( a + [b] for a in x[-1] for b in rgs[3]) )

, я получаю хороший результат.Однако, когда я пытаюсь обобщить это, выполняя

x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
for i in range(1,len(rgs)-1):
    x.append( ( a + [b] for a in x[-1] for b in rgs[i+1]) )

, я получаю список из 6 элементов:

[[0, 2, 2, 2],
 [0, 3, 2, 2],
 [0, 4, 2, 2],
 [1, 2, 2, 2],
 [1, 3, 2, 2],
 [1, 4, 2, 2]]

Кроме того, я замечаю, что все диапазоны, генерируемые послепервые два используют диапазон в rgs[-1] вместо правильных.Я изо всех сил пытаюсь понять, почему это происходит, поскольку я полагаю, что эти два примера кода идентичны, за исключением того, что последний является более общей формой для произвольного большого числа диапазонов.

Ответы [ 2 ]

0 голосов
/ 28 сентября 2018

Ваша проблема связана с созданием выражений генератора в цикле.Выражения генератора реализованы в виде функций, и, подобно функциям, они могут иметь «свободные» переменные, которые они ищут в содержащих пространствах имен.Выражения вашего генератора обращаются к i извне их определения, и в результате они в конечном итоге видят другое ожидаемое значение i.

Вот пример, который может быть легче понять:

def gen()
   print(i)
   yield 10

x = []
for i in range(3):
    x.append(gen())  # add several generators while `i` has several different values

for g in x:
    list(g)   # consume the generators, so they can print `i`

Здесь вместо того, чтобы использовать значение i для чего-то полезного, я написал функцию генератора, которая просто распечатывает его.Если вы запустите этот код, вы увидите, что все генераторы выводят 2, поскольку это значение i, когда они наконец запускаются (после окончания первого цикла).

Ваша ситуациянемного более тонкий, так как вы используете предыдущий генератор при создании следующего, но общая идея та же.Цикл выражения генератора, который, как вы ожидаете, будет превышать rgs[2], на самом деле превышает rgs[3], потому что он фактически просматривается с rgs[i+1] и i, увеличенными между временем, когда было объявлено выражение генератора, и когда оно было использовано.

0 голосов
/ 28 сентября 2018

Вы можете использовать itertools.product для вывода списка кортежей

Ввод:

import itertools

a= [range(0, 2), range(2, 5), range(4, 6), range(2, 3)]
list(itertools.product(*a))

Ввод:

[(0, 2, 4, 2),
 (0, 2, 5, 2),
 (0, 3, 4, 2),
 (0, 3, 5, 2),
 (0, 4, 4, 2),
 (0, 4, 5, 2),
 (1, 2, 4, 2),
 (1, 2, 5, 2),
 (1, 3, 4, 2),
 (1, 3, 5, 2),
 (1, 4, 4, 2),
 (1, 4, 5, 2)]

Я не получил тот же результатпри запуске вашего первого кода.Мне пришлось немного изменить это:

x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
x.append( ( a + [b] for a in x[-1] for b in rgs[2]) )
x =  list( a + [b] for a in x[-1] for b in rgs[3]) 

Большинство людей, которые не знают об itertools, сделали бы это следующим образом:

x=[]
for i0 in rgs[0]:
    for i1 in rgs[1]:
        for i2 in rgs[2]:
            for i3 in rgs[3]:
                x.append([i0,i1,i2,i3])

Или используя понимание списка (DONНЕ ДЕЛАЙТЕ ЭТО, это ОЧЕНЬ грязно выглядит):

[[i0,i1,i2,i3] for i3 in rgs[3] for i2 in rgs[2] for i1 in rgs[1] for i0 in rgs[0]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...