Скажем, у меня есть входные данные в виде списка таких кортежей:
[('a', True),
('b', False),
('c', True),
('d', False)]
Каждый кортеж, в котором вторым параметром указан True
, считается необязательным.
Теперь я хочу переставить эту структуру следующим образом:
False
, не изменяются (исчезают)Следовательно, результат приведенного выше примера должен выглядеть следующим образом:
[[('a', True),
('b', False),
('c', True),
('d', False)],
[('b', False),
('c', True),
('d', False)],
[('a', True),
('b', False),
('d', False)],
[('b', False),
('d', False)]]
Есть мысли, как элегантно решить эту проблему? Я пробовал использовать рекурсию, но не смог.
Я не уверен, что есть особенно элегантный способ. По идее, вам нужно вычислить набор мощности дополнительных элементов, но объединить его с необязательными элементами таким образом, чтобы удовлетворить ваши требования. Вот один из способов:
import itertools
a = [('a', True), ('b', False), ('c', True), ('d', False)]
optional_count = sum(optional for x, optional in a)
for include in itertools.product([True, False], repeat=optional_count):
include_iter = iter(include)
print([
(x, optional)
for x, optional in a
if not optional or next(include_iter)
])
печать
[('a', True), ('b', False), ('c', True), ('d', False)]
[('a', True), ('b', False), ('d', False)]
[('b', False), ('c', True), ('d', False)]
[('b', False), ('d', False)]
Цикл перебирает все кортежи, указывая, следует ли включать необязательные элементы:
True, True
True, False
False, True
False, False
Понимание списка в операторе печати включает в себя все необязательные элементы, а для дополнительных просматривается следующий доступный элемент из include
.
Вы можете сделать выбор, сделав копию списка уже сделанных выборов и добавив новое значение к копии, а затем объединив его со списком уже сделанных выборов.
Извините за это ужасное объяснение, но сейчас я не могу придумать ничего лучшего. Не стесняйтесь редактировать, если сможете придумать что-нибудь получше. В противном случае я надеюсь, что приведенная ниже диаграмма и код лучше справятся с объяснением моего решения.
[]
-------------------------------------------
[] [0]
---------------------------- ---------------------------
[] [1] [0] [0, 1]
... ... ... ...
Основная идея состоит в том, чтобы перебрать каждый элемент, клонировать все частичные решения и добавить элемент к клонированным решениям.
def choose(arr):
res = [[]]
for t in arr:
if t[1]:
# make a copy of all found solutions
clone = [list(c) for c in res]
# append the new value to the original solutions
for l in res:
l.append(t)
# merge solution-list with the list of copies
res += clone
else:
# non-optional element => just add the element to all solutions
for l in res:
l.append(t)
return res
Выход:
print('\n'.join(str(l) for l in choose([('a', True), ('b', False), ('c', True), ('d', False)])
[('a', True), ('b', False), ('c', True), ('d', False)]
[('b', False), ('c', True), ('d', False)]
[('a', True), ('b', False), ('d', False)]
[('b', False), ('d', False)]
На самом деле есть хорошее рекурсивное решение, о котором я только что подумал:
def choices(a):
if not a:
yield []
return
head, *tail = a
if head[1]:
yield from choices(tail)
for tail_choice in choices(tail):
yield [head] + tail_choice
Это создает ленивый генератор для всех списков кортежей:
>>> list(choices(a))
[[('b', False), ('d', False)],
[('b', False), ('c', True), ('d', False)],
[('a', True), ('b', False), ('d', False)],
[('a', True), ('b', False), ('c', True), ('d', False)]]
Итеративное решение в моем первом ответе является наиболее эффективным, поскольку оно не копирует без необходимости какие-либо списки. Но производительность может не иметь значения для вашего варианта использования, поэтому просто выберите то, что вы считаете наиболее читаемым.
Ich komme übrigens aus der Nähe von Neumünster. Da fragt man in die weite Welt hinein und bekommt die Antwort von jemandem aus Preetz. :)
@systderr Kleine Welt :)
без надобности вычисляет choices(tail)
дважды - в противном случае хороший ответ
Да, это похоже на решение, которое я придумал. Про оператора
yield from
не знал. Я тоже выложу свой. Может быть, мы сможем обсудить, какое на самом деле лучшее решение здесь, поскольку они все решают его. Хотя мне очень нравятся рекурсивные.