关于python:如何比较列表中的每个项目与其余项目,只有一次?

How to compare each item in a list with the rest, only once?

假设我有一个我要比较的数组/列表。 在我更熟悉的语言中,我会做类似的事情

1
2
3
for (int i = 0, i < mylist.size(); i++)
    for (int j = i + 1, j < mylist.size(); j++)
        compare(mylist[i], mylist[j])

这确保我们只比较每对一次。 对于某些上下文,我正在对列表中包含的一堆对象进行冲突检测。 对于检测到的每个碰撞,描述碰撞的小"碰撞"对象被附加到列表,然后另一个例程循环解决每个碰撞(取决于两个碰撞对象的性质)。 显然,我只想报告每次碰撞一次。

现在,这样做的pythonic方法是什么,因为Python倾向于使用迭代器而不是循环索引?

我有以下(错误)代码:

1
2
3
for this in mylist:
    for that in mylist:
        compare(this, that)

但这显然会在每次碰撞中发生两次,这在尝试解决它们时会导致一些奇怪的行为。 那么这里的pythonic解决方案是什么?


当然,这将生成每对两次,因为每个for循环将遍历列表中的每个项目。

你可以在这里使用一些itertools魔法来生成所有可能的组合:

1
2
3
import itertools
for a, b in itertools.combinations(mylist, 2):
    compare(a, b)

itertools.combinations将每个元素与iterable中的每个其他元素配对,但只能配对一次。

您仍然可以使用嵌套的for循环使用基于索引的项目访问来编写它,相当于您习惯使用的内容:

1
2
3
for i in range(len(mylist)):
    for j in range(i + 1, len(mylist)):
        compare(mylist[i], mylist[j])

当然,这可能看起来不那么好和pythonic,但有时这仍然是最容易和最易理解的解决方案,所以你不应回避解决这样的问题。


使用itertools.combinations(mylist, 2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mylist = range(5)
for x,y in itertools.combinations(mylist, 2):
    print x,y

0 1
0 2
0 3
0 4
1 2
1 3
1 4
2 3
2 4
3 4


我认为在外部循环上使用枚举并使用索引来切割内部循环上的列表是非常Pythonic:

1
2
3
for index, this in enumerate(mylist):
    for that in mylist[index+1:]:
        compare(this, that)

此代码将计算频率并删除重复元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from collections import Counter

str1='the cat sat on the hat hat'

int_list=str1.split();

unique_list = []
for el in int_list:

    if el not in unique_list:
        unique_list.append(el)
    else:
        print"Element already in the list"

print unique_list

c=Counter(int_list)

c.values()

c.keys()

print c