Как «сплющить» генераторы в python? - PullRequest
0 голосов
/ 21 января 2019

У меня проблема с "выравниванием" некоторых генераторов в python.Вот мой код:

import itertools as it
test = [[1,2,3],[4,5],[6,7,8]]
def comb(possible):
    if len(possible) != 1:
        for a in possible[0]:
            yield from it.product((a,), comb(possible[1:]))
    else:
        yield from possible[0]
list(comb(test))

, который дает мне:

[(1, (4, 6)),
(1, (4, 7)),
(1, (4, 8)),
(1, (5, 6)),
(1, (5, 7)),
(1, (5, 8)),
(2, (4, 6)),
(2, (4, 7)),
(2, (4, 8)),
(2, (5, 6)),
(2, (5, 7)),
(2, (5, 8)),
(3, (4, 6)),
(3, (4, 7)),
(3, (4, 8)),
(3, (5, 6)),
(3, (5, 7)),
(3, (5, 8))]

Однако я хочу что-то вроде:

[(1, 4, 6),
(1, 4, 7),
(1, 4, 8),
(1, 5, 6),
(1, 5, 7),
(1, 5, 8),
(2, 4, 6),
(2, 4, 7),
(2, 4, 8),
(2, 5, 6),
(2, 5, 7),
(2, 5, 8),
(3, 4, 6),
(3, 4, 7),
(3, 4, 8),
(3, 5, 6),
(3, 5, 7),
(3, 5, 8)]

В общем, функция должна дать мнегенераторы для всех возможных путей прохождения списка, т.е. from test[0] -> test[1] -> ... -> test[n], где n равно len(test).Здесь на каждом шаге он получает один элемент.

Аналогично тому, что возвращает следующая функция, только с генераторами:

def prod(possible):
    if len(possible) != 1:
        b = []
        for i in range(len(possible[0])):
            for x in prod(possible[1:]):
                if len(possible) == 2:
                    b += [[possible[0][i]]+[x]]
                else:
                    b += [[possible[0][i]]+x]
        return b
    else:
        return possible[0]
prod(test)

Я играл с it.chain и it.chain.from_iterable, ноне могу заставить его работать.Проблема в том, что мой «тестовый» список имеет переменный размер и длину, и поэтому мне приходится делать все это рекурсивно.

Редактировать:

itertools.product(*test)

работы, как указано Джоном Коулманом

1 Ответ

0 голосов
/ 22 января 2019

Вот один из способов вычисления product списков без использования встроенного

def product (*iters):
  def loop (prod, first = [], *rest):
    if not rest:
      for x in first:
        yield prod + (x,)
    else:
      for x in first:
        yield from loop (prod + (x,), *rest)
  yield from loop ((), *iters)

for prod in product ("ab", "xyz"):
  print (prod)

# ('a', 'x')
# ('a', 'y')
# ('a', 'z')
# ('b', 'x')
# ('b', 'y')
# ('b', 'z')

. В python мы можем собирать выходные данные генератора в списке с помощью конструктора list.,Обратите внимание, что мы также можем рассчитать произведение более чем двух входных данных, как показано ниже

print (list (product ("+-", "ab", "xyz")))
# [ ('+', 'a', 'x')
# , ('+', 'a', 'y')
# , ('+', 'a', 'z')
# , ('+', 'b', 'x')
# , ('+', 'b', 'y')
# , ('+', 'b', 'z')
# , ('-', 'a', 'x')
# , ('-', 'a', 'y')
# , ('-', 'a', 'z')
# , ('-', 'b', 'x')
# , ('-', 'b', 'y')
# , ('-', 'b', 'z')
# ]

Поскольку product принимает список iterables , в продукте может использоваться любой повторяемый вход.Они могут даже смешиваться, как показано ниже

print (list (product (['@', '%'], range (2), "xy")))
# [ ('@', 0, 'x')
# , ('@', 0, 'y')
# , ('@', 1, 'x')
# , ('@', 1, 'y')
# , ('%', 0, 'x')
# , ('%', 0, 'y')
# , ('%', 1, 'x')
# , ('%', 1, 'y')
# ]

Поскольку product определен как генератор, нам предоставляется большая гибкость даже при написании более сложных программ.Рассмотрим эту программу, которая находит прямоугольные треугольники, составленные из целых чисел, пифагорейская тройка .Также обратите внимание, что product позволяет вам повторять итерацию в качестве ввода, как показано в product (r, r, r) ниже

def is_triple (prod):
  (a,b,c) = prod
  return a * a + b * b == c * c

def solver (n):
  r = range (1,n)
  for p in product (r, r, r):
    if is_triple (p):
      yield p

print (list (solution in solver (20)))
# (3, 4, 5)
# (4, 3, 5)
# (5, 12, 13)
# (6, 8, 10)
# (8, 6, 10)
# (8, 15, 17)
# (9, 12, 15)
# (12, 5, 13)
# (12, 9, 15)
# (15, 8, 17)

Для дополнительного объяснения и способа увидеть, как это сделать без использования генераторов, просмотрите этот ответ .

...