繁体   English   中英

不能从两个列表中删除相似的元素

[英]Can not remove similar elements from two lists

我有数组一:a1 = [1,2,3],数组二:a2 = [1,2,3]。 我必须比较这两个数组并从 a1 中删除所有相似的元素。 同时我必须返回a1并且我不能使用这些是要求的附加库。 我试图以这种方式做到这一点:

    for i in a1:
        for j in a2:
            if i == j:
                a1.remove(j)

这里的问题是它返回 [2] 而不是 [](空数组)

附注。 我知道类似的问题,但我在那里找不到解决方案如何在迭代时从列表中删除项目?

我会为此使用 Sets

>>> a1 = [1,2,3,4]
>>> a2 = [1,2,3,4]
>>> a1 = list(set(a1) - set(a2))
>>> a1
[]
>>> a1 = [1,2,3,4,5]
>>> a1 = list(set(a1) - set(a2))
>>> a1
[5]

对于较小的数据集,这可能效率低下,但对于较大的数据集,我发现它的效率要高得多(基于时间)。

使用 timeit 和 time 定时运行。 使用另一个答案中的方法

time python -m timeit  -n 10 -s "a1 = range(100000); a2=range(100000) " -s "for i in a1:"  -s "    if i in a2: a1.remove(i)"
10 loops, best of 3: 0.0954 usec per loop

real    2m34.144s
user    2m32.468s
sys 0m0.468s

使用上述方法定时运行

time python -m timeit -n 10 "a1 = range(1000000); a2=range(1000000) ; a1 = list(set(a1) - set(a2)) ; "
10 loops, best of 3: 109 msec per loop

real    0m3.453s
user    0m3.090s
sys 0m0.350s

尝试这个:

for i in a1:
  if i in a2:
    a1.remove(i)

有一百种方法可以解决这个问题,但这是我最喜欢的一种:

a1 = list(filter(lambda e: e not in a2, a1))

您可能可以通过以下方式做到这一点:

a1 = [x for x in a1 if x not in a2]

你会得到[2]的原因是你正在从a中删除元素,而继续迭代a这很可能总是产生问题。

这是我们所期望的:

# sudo code
a[0] == b[0] ? if not then continue iterating array b; if yes, a.remove(b[0])
a[0] == b[1] ? if not then continue iterating array b; if yes, a.remove(b[1])
...

好吧,但程序不会像我们预期的那样做:(

据推测,这与for循环在 python 中的工作机制有关。 我对此做了一些简短的研究,但没有找到确切的答案。 然而,根据[2]的结果,地下可能发生的事情如下(免责声明:我没有查源代码,我鼓励你,所以下面的推论可能是错误的......)

因为我们都知道 Python 来自 C,所以 Python 的for循环只是 C 的简写。 在 C 中,通常我们按索引进行迭代,例如for (int index=0; index < size; index++) 在 Python 中,我们似乎省略了索引,只生成了值,但很可能 Python 的for循环仍然在带索引的for循环下运行,只是我们在那里没有看到它。 这种机制的结果是,当您在第一次迭代中执行a.remove(1)时,您将a的长度缩短到 2,同时索引从0增加到1 在第二次迭代期间,程序正在寻找a[1] ,其中1是从0递增的索引。 由于您删除了a的第一个元素,当前a[1]变成了前a[2] ,它指向值3 然后,您再次将 a 中的3ba所有值进行比较,那么它肯定可以找到匹配项并且也会删除 3。 现在它打破了条件index < size (实际上 index 已经大于 size 因为在两次删除后size已经减少到1 ,并且index会从1增加到2 )。 因此,迭代被称为停止。 现在,当您评估a它会产生[2] ,因为原始的a[1] 2被跳过了该过程。

可以说明这一点的示例代码如下,我已经尝试过:

a = [1, 2, 3]
for n in a:
    print(n)
    a.remove(n)

你会看到只打印了13 ,这证明2确实被跳过了。

这可以通过以下代码更好地解释:-

for i in a1:
    print('a1 = ', a1)
    print('i = ', i)
    if i in a2:
        a1.remove(i)
        print('After removing, a1 = ', a1)

上面的代码输出

a1 =  [1, 2, 3]
i =  1
After removing, a1 =  [2, 3]
a1 =  [2, 3]
i =  3
After removing, a1 =  [2]

可以看出,在第一次删除 1 之后,现在 i 变为 3,而不是 2。这是因为 Python 在循环开始时修复了迭代器的值。 在这种情况下,它将 i 从 0 固定到 2,但是由于a1.remove()减少了 a1 的长度,它应该检查 2,但它正在检查第一个索引,即a1.remove() i = i - 1不能放在这里因为它不起作用。 为此,可以使用以下代码

i = 0
while i < len(a1):
    if a1[i] in a2:
        a1.remove(a1[i])
        i = i - 1
    i = i + 1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM