如何将列表中的每个项目与其余项目进行比较,只进行一次?

Bog*_*ist 57 python

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

for (int i = 0, i < mylist.size(); i++)
    for (int j = i + 1, j < mylist.size(); j++)
        compare(mylist[i], mylist[j])
Run Code Online (Sandbox Code Playgroud)

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

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

我有以下(错误)代码:

for this in mylist:
    for that in mylist:
        compare(this, that)
Run Code Online (Sandbox Code Playgroud)

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

pok*_*oke 102

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

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

import itertools
for a, b in itertools.combinations(mylist, 2):
    compare(a, b)
Run Code Online (Sandbox Code Playgroud)

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


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

for i in range(len(mylist)):
    for j in range(i + 1, len(mylist)):
        compare(mylist[i], mylist[j])
Run Code Online (Sandbox Code Playgroud)

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

  • @3kstc我从问题中得到了这一点。将其替换为您想要对每对项目执行的任何操作。 (3认同)
  • 如果你想要索引,只需在 itertools.combinations(index, 2):` 中添加 `index=range(len(mylist))` 和 `for a, b:` 然后 `compare(mylist[a], mylist[b])` . 现在您可以使用索引从 `mylist` 中获取元素。 (3认同)
  • @poke `名称'比较'未定义`我该如何解决这个问题? (2认同)

shx*_*hx2 21

使用 itertools.combinations(mylist, 2)

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
Run Code Online (Sandbox Code Playgroud)

  • 这真的很有帮助。谢谢。 (2认同)

ala*_*lan 7

我认为在外部循环上使用枚举并在内部循环上使用索引对列表进行切片是相当Pythonic的:

for index, this in enumerate(mylist):
    for that in mylist[index+1:]:
        compare(this, that)
Run Code Online (Sandbox Code Playgroud)

  • @Ghos3t 肯定是一个方便的功能。如果您想跳过最后一次迭代,因为没有必要,您只需从枚举列表中切掉最后一个元素即可:`mylist[:-1]`。 (2认同)