我忽略了您的 intersect 函数的语义。
如果它是关于 python 中的循环的问题,对于您的问题而言,这无关紧要。
如果这是关于您的 intersect 函数在此特定用例中的语义的问题,则说明您没有提供足够的信息。
一般而言,在循环访问可迭代对象(如列表)时修改它是危险且不鼓励的。
例如,如果我们写这个循环
xs = [ 1 ]
for x in xs:
xs.append(x+1)
python 实际上会无限循环。
list 对象的迭代器将继续抓取新添加的元素。
您可以通过在完成迭代之前不修改 lst 来解决此问题:
to_remove = []
for f1 in lst:
# because lst is not being modified, we have to manually skip
# elements which we will remove later
# the performance difference is negligible on small lists
if f1 in to_remove:
continue
for f2 in lst:
# also skip f2s which we removed
if f2 in to_remove:
continue
# note that I collapsed two of your conditions here for brevity
# this is functionally the same as what you wrote, but looks neater
if f1 != f2 and f1.intersect(f2):
if f1.score >= f2.score:
to_remove.append(f2)
else:
to_remove.append(f1)
lst = [x for x in lst if x not in to_remove]
请注意,此解决方案远非完美。
我仍然有两个主要问题:使用list 而不是set 代替to_remove,这样可以更好地表达您的意思,以及通过执行一个简单的嵌套循环来重复比较。
改进这一点的下一步是将to_remove 替换为set 对象,并减少过度循环。
我们可以使用列表切片和方便的enumerate 函数轻松地做到这一点。
所以,第 1 部分正在切换到 sets:
to_remove = set()
for f1 in lst:
if f1 in to_remove:
continue
for f2 in lst:
if f2 in to_remove:
continue
if f1 != f2 and f1.intersect(f2):
if f1.score >= f2.score:
to_remove.add(f2)
else:
to_remove.add(f1)
lst = [x for x in lst if x not in to_remove]
第二个组件,使用enumerate,依赖于切片符号的知识。
如果您不熟悉它,我建议您阅读它。
一个很好的 SO 帖子:Explain Python's slice notation
不管怎样,我们开始吧:
to_remove = set()
# with enumerate, we walk over index, element pairs
for index,f1 in enumerate(lst):
if f1 in to_remove:
continue
# parens in slicing aren't required, but add clarity
for f2 in lst[(index+1):]:
if f2 in to_remove:
continue
# no need to check for f1 == f2, since that's now impossible
# unless elements are duplicated in your list, which I assume
# is not the case
if f1.intersect(f2):
if f1.score >= f2.score:
to_remove.add(f2)
else:
to_remove.add(f1)
# still probably the clearest/easiest way of trimming lst
lst = [x for x in lst if x not in to_remove]
如果您实际上不需要 lst 作为列表,您可以更进一步,也将其设为 set。
这开启了利用内置集差操作的可能性,但这使得循环变得更加困难。
to_remove = set()
# still iterate over it as a list, since we need that to be able to slice it
# if you replace it with a set at the outset, you can always listify it
# by doing `list(lst_as_set)`
for index,f1 in enumerate(lst):
if f1 in to_remove:
continue
# parens in slicing aren't required, but add clarity
for f2 in lst[(index+1):]:
if f2 in to_remove:
continue
# no need to check for f1 == f2, since that's now impossible
if f1.intersect(f2):
if f1.score >= f2.score:
to_remove.add(f2)
else:
to_remove.add(f1)
# yep, we can turn the set into a list more or less trivially
# (usually, duplicate elements make things complicated)
keep = set(lst)
# set difference can be done with the minus sign:
# https://docs.python.org/2/library/stdtypes.html#set
keep = keep - to_remove
编辑:
在我最初的回答中,在将元素添加到 to_remove 后,我没有将它们从考虑中删除