列出python中的理解行为

时间:2014-05-19 16:29:39

标签: python list-comprehension enumerate

我正在与Codeskulptor合作解决岩石碰撞问题。我想检查岩石之间的碰撞,我的岩石列在清单中。我想出了构建组合列表然后检查碰撞的解决方案。 我没有itertools可用。 我的组合列表是这样创建的:

def combinations(items):
    n_items = [(n,item) for n,item in enumerate(items)]
    return [(item,item2) for n,item in n_items for m,item2 in n_items[n:] if n != m]

letters = ['A','B','C','D']
print combinations(letters)

[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')] 

结果还可以。

在尝试使用函数之前,我尝试在一个衬里中执行此操作:

def combinations2(items):
    return [(item,item2) for n,item in enumerate(items) for m,item2 in enumerate(items[n:]) if n != m]

letters = ['A','B','C','D']
print combinations2(letters)

但结果完全不同而且错误:

[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'B'), ('B', 'D'), ('C', 'C'), ('C', 'D'), ('D', 'D')]

列表理解对我来说仍然是一个黑魔法。我无法解释这种行为,想了解错误了。 我知道我的双线解决方案要快得多,因为枚举只执行一次而不是使用。但错误的输出对我来说是无法解释的,特别是当缺少BC时,BB CC DD会在AA缺失的同时丢失。

有人可以帮助我吗?

3 个答案:

答案 0 :(得分:6)

理解列表理解时要做的第一件事是将其扩展为常规的for循环集。从左到右阅读循环并相应地嵌套。

工作代码:

def combinations(items):
    n_items = []
    for n,item in enumerate(items):
        n_items.append((n,item))
    result = []
    for n, item in n_items:
        for m, item2 in n_items[n:]:
            if n != m:
                result.append((item, item2))
    return result

并且您的尝试无效:

def combinations2(items):
    result = []
    for n, item in enumerate(items):
        for m, item2 in enumerate(items[n:]):
            if n != m:
                result.append((item, item2))
    return result

也许这样可以更容易地看出两个版本之间出现了什么问题。

您的版本切片只是 items,而不是enumerate() 生成的索引。原始版本会将[(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D')]切换为[(1, 'B'), (2, 'C'), (3, 'D')]等,而您的版本会将该切片重新编号为[(0, 'B'), (1, 'C'), (2, 'D')]。这反过来会导致错误的输出。

通过向enumerate()函数添加第二个参数来启动更高索引处的内部循环,该函数是开始编号的索引:

def combinations2(items):
    result = []
    for n, item in enumerate(items):
        for m, item2 in enumerate(items[n:], n):
            if n != m:
                result.append((item, item2))
    return result

回到单行:

def combinations2(items):
    return [(item, item2) for n, item in enumerate(items) for m, item2 in enumerate(items[n:], n) if n != m]

然后这可以正常工作:

>>> def combinations2(items):
...     return [(item, item2) for n, item in enumerate(items) for m, item2 in enumerate(items[n:], n) if n != m]
... 
>>> letters = ['A','B','C','D']
>>> combinations2(letters)
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]

请注意,您可以将其简化为进一步; n == m True的唯一时间是每个内循环的第一次迭代。只需将内部列表的items列表进一步切片;从1开始外部enumerate(),放弃内部enumerate()并放弃n != m测试:

def combinations3(items):
    result = []
    for n, item in enumerate(items, 1):
        for item2 in items[n:]:
            result.append((item, item2))
    return result

或作为列表理解:

def combinations3(items):
    return [(item, item2) for n, item in enumerate(items, 1) for item2 in items[n:]]

答案 1 :(得分:1)

跳过迭代器中的冲突。

>>> letter = ['A', 'B', 'C', 'D']
>>> list ( (x,y) for n, x in enumerate(letter) for y in letter[n+1:])
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]

答案 2 :(得分:0)

假设您只想获得组合列表。

def combinations2(items):
    return filter(lambda (i,j): i <> j, [(i,j) for i in items for j in items])

letters = ['A','B','C','D']
print combinations2(letters)

我得到的输出是:

[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'C'), ('B', 'D'), ('C', 'A'), ('C', 'B'),    ('C', 'D'), ('D', 'A'), ('D', 'B'), ('D', 'C')]