在刷PAT乙级题库1028人口普查的时候遇到这个问题,使用for循环过滤不想要的数据,但没能完全过滤,现将问题和解决办法记录如下。
一、问题说明
为了方便说明问题,只使用简单示例,代码如下:
a=[i+1 for i in range(6)]
print(a)
for i in a:
if i>3:
a.remove(i)
print(a)
程序运行结果如下:
我们的目的是删除列表里面大于3的数字,但是只删除了4和6,落下了5,这个和C++里面的迭代器有点像。
那5是怎么被落下的呢?
- 大于3的第一个数字是4,列表删除4;
- 同时5和6往前挪一个位置,a[3]的值变成了5;(坑没变,人往前挪)
- for循环i变成a[4](索引自动往后移动),值为6,继续判断和删除;
用pop(index)如果不注意,同样会出现相同的问题。
二、解决办法
1、倒序循环
为了方便讲解,我们把列表原先数字顺序颠倒下:
a=[6-i for i in range(6)]
print(a)
# for i in a:
# if i>3:
# a.remove(i)
for i in range(len(a)-1,-1,-1):
if a[i]>3:
a.pop(i)
print(a)
程序运行结果如下:
为什么这次没出问题?
- 大于3的第一个数字是4,删除4的同时3、2、1同时往前移动一步;
- 此时a[2]=3,索引递减指向了a[1],并没有像之前“落下”数字。
如果删除元素,后面的元素往前挪,但之前的索引是往后挪,两者方向不一致,才“落下”某些数字,我们只要保证索引移动的方向和删元素后的移动方向一致就能避免这个问题。
2、拷贝列表
示例如下:
a=[6-i for i in range(6)]
print(a)
# for i in a:
# if i>3:
# a.remove(i)
for i in a[:]:
if i>3:
a.remove(i)
print(a)
程序运行结果:
a[:]就是列表a的一个拷贝,循环和操作使用不同的列表。但是拷贝列表需要耗费内存,增加程序运行时间,列表里面元素不重复才能使用这种方法。
三、参考资料
Python的list循环遍历中,删除数据的正确方法
PAT1028人口普查的代码可查询:python实现PAT乙级算法题库