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), (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)  
4

3 回答 3

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
于 2011-06-03T08:53:47.750 回答
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
于 2011-06-03T09:10:24.037 回答
2

使用 tee'ed 迭代器的@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))
于 2012-08-13T10:17:17.233 回答