python: как сделать продукт из итераций без повторения пунктов? - PullRequest
1 голос
/ 03 июня 2011

Мне нужна функция, которая работает аналогично 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), (page1, page2, page3).
Теперь предположим, что я хочу собрать описания для каждой страницы:
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)  

Ответы [ 3 ]

2 голосов
/ 03 июня 2011

На основании вашего комментария, в котором указано "потому что (None, None, 8) не происходит последовательно" , я предполагаю, что вы хотите None - определить элементы, которые отображаются выход непосредственно перед.

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
2 голосов
/ 13 августа 2012

Сортировка функциональной версии ответа @ ShawnChin с использованием итератора:

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 голосов
/ 03 июня 2011
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
...