@solub, your solution is buggy!
When we delete an item from a list, all elements w/ a higher index than the deleted item got their indices subtracted by 1!
They’re left-shifted, and therefore, the next iteration skips an item; which now got the same index as the just deleted item.
You can see that bug in this sample loop below: :
nums = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for n in nums:
print n
nums.remove(n)
print nums # [1, 3, 5, 7, 9]
The code above was supposed to remove all items from the list. But accomplished only half of it!
There are some techniques to pull that out though.
In Java, my favorite approach is to simply traverse the list backwards instead.
Python got reversed(), which returns a backwards iterator for a sequence.
nums = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for n in reversed(nums):
print n
nums.remove(n)
print nums # []
Now it’s finally worked! All items got backwards removed w/o any errors!
However, remove() ( and also index() ) is a slow method, given it’s gotta search the whole list for the target item.
A better approach is to use del
, since it removes an item by its index.
For that, we’re gonna need to use enumerate() and also turn it into a sequence before calling reversed():
nums = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for i, n in reversed(tuple(enumerate(nums))):
print i, n
del nums[i]
print nums # []
Here’s @warsoldier011’s excerpt adapted to use a backwards loop w/ del
:
for i, e in reversed(tuple(enumerate(enemies))):
e.display()
e.goToTarget(myPlayer.xpos, myPlayer.ypos)
# for j, l in reversed(tuple(enumerate(lasers))): # no need w/ break
for j, l in enumerate(lasers):
if l.collision(e.xpos, e.ypos, e.r):
del lasers[j]
del enemies[i]
break
l.display()
As a bonus, here’s a more advanced performant example, which replaces the item being removed w/ its list’s tail element; so there’s no index left-shifting at all:
for i in range(len(enemies) - 1, -1, -1):
e = enemies[i]
e.display()
e.goToTarget(myPlayer.xpos, myPlayer.ypos)
# for j in range(len(lasers) - 1, -1, -1): # no need to be reversed w/ break
for j in range(len(lasers)):
l = lasers[j]
if l.collision(e.xpos, e.ypos, e.r):
tail = lasers.pop()
if tail is not l: lasers[j] = tail
tail = enemies.pop()
if tail is not e: enemies[i] = tail
break
l.display()