python: как сделать продукт из итераций без повторения пунктов?

1

Мне нужна функция, которая работает так же, как и 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)  
  • 0
    Почему ничего None появляется в вашем результате?
  • 1
    Как вы говорите, что эти результаты не повторяют пункты? 6 появляется, по крайней мере, три раза, и 5 два раза, в первом наборе, а второй набор имеет по крайней мере три 8 и два 7 .
Показать ещё 3 комментария
Теги:
itertools

3 ответа

2
Лучший ответ

На основании вашего комментария, в котором указано: "потому что (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
  • 0
    спасибо за ваше решение. Я уточнил свой вопрос. Я посмотрю, является ли ваше решение тем, которое я хочу.
  • 0
    здорово! Спасибо за отличное решение. :)
2

Отклоните функциональную версию ответа @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))
2
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
  • 0
    Большое спасибо за ваше решение. Это близко к тому, что я хочу. Я уточнил свой вопрос. Вы можете пройти через мое дальнейшее редактирование. Тем временем я посмотрю, соответствует ли ваше решение тому, что я хочу.
  • 0
    @Salil: эта функция будет работать отлично, без изменений для вашего сценария. Просто накормите его последовательностями строк вместо целых чисел.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню