Мне нужна функция, которая работает так же, как и itertools.product, но без повторения элементов.
Например:
no_repeat_product((1,2,3), (5,6))
= ((1,5), (None,6), (2,5), (None,6), ...(None,6))
no_repeat_product((1,2,3), (5,6), (7,8))
= ((1,5,7), (None,None,8), (None,6,7), (None,None,8), ...(None,None,8))
Любые идеи?
Изменить:
моя формулировка была неправильной. Я имел в виду , не повторяя числа, которые совпадают с последовательными выходными значениями.
Например,
itertools.product((1,2,3), (4,5), (6,7) is
(1,4,6)
(1,4,7), etc
Здесь 1,4 появляется дважды на выходе. Итак, я хочу пропустить запись чисел, когда они такие же, как и раньше. Итак, я хочу:
(1,4,6)
(None,None,7)
Когда он равен None, понимается, что он аналогичен предыдущему элементу в результате.
Дальнейшее редактирование:
Мои объяснения по-прежнему не были ясны.
Предположим, что у меня есть список книг, номеров глав и номеров страниц. Предположим, что каждая книга имеет одинаковое количество глав, и каждая глава имеет одинаковое количество страниц.
Итак, списки (book1, book2, book3), (chap1, chap2), (стр. 1, стр. 2, стр. 3).
Теперь предположим, что хочу собирать описания для каждой страницы:
itertools.product даст мне:
(book1, chap1, page1), (book1, chap1, page2)..... (book3, chap2, page3)
Если я упорядочил эти страницы подряд, мне не нужно повторять описания. Итак, если книга и глава одинаковы, на второй странице мне не нужно иметь имена книг и разделов Таким образом, вывод должен быть:
(book1, chap1, page1), (None, None, page2), ..
(when the pages of first chapter are over..) (None, chap2, page1), (None, None, page2)......
(when the chapters of the first book are over..)(book2, chap1, page1)..............
(None, None, page3)
На основании вашего комментария, в котором указано: "потому что (None, None, 8) не происходит последовательно", я предполагаю, что вы хотите только None- ify-элементы, которые появляются на выходе непосредственно перед этим.
def no_repeat_product(*seq):
previous = (None,)*len(seq)
for vals in itertools.product(*seq):
out = list(vals)
for i,x in enumerate(out):
if previous[i] == x:
out[i] = None
previous = vals
yield(tuple(out))
Или, если вы предпочитаете более компактную и эффективную (но менее читаемую) версию:
def no_repeat_product(*seq):
previous = (None,)*len(seq)
for vals in itertools.product(*seq):
out = tuple((y,None)[x==y] for x,y in itertools.izip(previous, vals))
previous = vals
yield(out)
Они оба делают одно и то же и дают следующие результаты:
for x in no_repeat_product((1,2,3), (5,6), (7,8)):
print x
Вывод:
(1, 5, 7)
(None, None, 8)
(None, 6, 7)
(None, None, 8)
(2, 5, 7)
(None, None, 8)
(None, 6, 7)
(None, None, 8)
(3, 5, 7)
(None, None, 8)
(None, 6, 7)
(None, None, 8)
Пример в контексте вашего обновленного вопроса:
books = ("Book 1", "Book 2")
chapters = ("Chapter 1", "Chapter 2")
pages = ("Page 1", "Page 2", "Page 3")
s1 = max(map(len, books)) + 2 # size of col 1
s2 = max(map(len, chapters)) + 2 # size of col 2
x = lambda s, L: (s, "")[s == None].ljust(L) # Left justify, handle None
for book, chapter, page in no_repeat_product(books, chapters, pages):
print x(book, s1), x(chapter, s2), page
Это дает вам:
Book 1 Chapter 1 Page 1
Page 2
Page 3
Chapter 2 Page 1
Page 2
Page 3
Book 2 Chapter 1 Page 1
Page 2
Page 3
Chapter 2 Page 1
Page 2
Page 3
Отклоните функциональную версию ответа @ShawnChin, используя итератор tee'ed:
from itertools import product,tee,izip
def product_without_repeats(*seq):
previter,curriter = tee(product(*seq))
try:
yield next(curriter)
except StopIteration:
pass
else:
for prev,curr in izip(previter,curriter):
yield tuple(y if x!=y else None for x,y in izip(prev,curr))
def no_repeat_product(*seq):
def no_repeat(x, known):
if x in known:
return None
else:
known.add(x)
return x
known = set()
for vals in itertools.product(*seq):
yield tuple(no_repeat(x, known) for x in vals)
Это не возвращает значение, которое уже было замечено раньше. Это то, что вы хотите?
Если вы хотите ограничить повторение значения, появившегося в предыдущем наборе результатов, это можно сделать следующим образом:
def no_repeat_product(*seq):
prev = None
for vals in itertools.product(*seq):
if prev is None:
yield vals
else:
yield tuple((x if x != y else None) for x, y in zip(vals, prev))
prev = vals
None
появляется в вашем результате?6
появляется, по крайней мере, три раза, и5
два раза, в первом наборе, а второй набор имеет по крайней мере три8
и два7
.