21

このプログラムを実行するたびに、次のエラーが発生します。

ValueError: list.remove(x): x not in list

ボルトが当たるたびに、エイリアン 1 体のヘルスを下げようとしています。ヘルスが の場合、その 1 つのエイリアンも破壊する必要があります<= 0。同様に、ボルトも破壊されます。これが私のコードです:

def manage_collide(bolts, aliens):
    # Check if a bolt collides with any alien(s)
    for b in bolts:
        for a in aliens:
            if b['rect'].colliderect(a['rect']):
                for a in aliens:
                    a['health'] -= 1
                    bolts.remove(b)
                    if a['health'] == 0:
                        aliens.remove(a)
    # Return bolts, aliens dictionaries
    return bolts, aliens

これValueErrorは回線上で発生しますaliens.remove(a)。明確にするために、aliensboltsは両方とも辞書のリストです。

私は何を間違っていますか?

4

5 に答える 5

41

ループしているリストから項目を削除しないでください。代わりにコピーを作成します。

for a in aliens[:]:

for b in bolts[:]:

ループ中にリストを変更すると、ループに影響します。

>>> lst = [1, 2, 3]
>>> for i in lst:
...     print i
...     lst.remove(i)
... 
1
3
>>> lst
[2]

2 回ループしているリストから項目を削除すると、さらに複雑になり、ValueError が発生します。

>>> lst = [1, 2, 3]
>>> for i in lst:
...     for a in lst:
...         print i, a, lst
...         lst.remove(i)
... 
1 1 [1, 2, 3]
1 3 [2, 3]
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
ValueError: list.remove(x): x not in list

ループのレベルで変更しているリストのコピーを作成すると、問題を回避できます。

>>> lst = [1, 2, 3]
>>> for i in lst[:]:
...     for i in lst[:]:
...         print i, lst
...         lst.remove(i)
... 
1 [1, 2, 3]
2 [2, 3]
3 [3]

衝突した場合、エイリアンを傷つけるループではなく、bボルトを一度外すだけで済みます。後でエイリアンを個別に一掃します。

def manage_collide(bolts, aliens):
    for b in bolts[:]:
        for a in aliens:
            if b['rect'].colliderect(a['rect']) and a['health'] > 0:
                bolts.remove(b)
                for a in aliens:
                    a['health'] -= 1
    for a in aliens[:]:
        if a['health'] <= 0:
            aliens.remove(a)
    return bolts, aliens
于 2013-01-02T17:54:57.217 に答える
2

これを引き起こしているコードにバグがあります。単純化されたコードは次のようになります。

for b in bolts:
  for a in aliens:
    for a in aliens:
      bolts.remove(b)

aliensこれにより、 のエントリごとに複数回ループしますb。最初のループで b を削除するaliensと、2 回目のループでエラーが発生します。

いくつか修正する必要があります。まず、内側のループをaliens以外のものを使用するように変更しますa

for b in bolts:
  for a in aliens:
    for c in aliens:
      if hit:
        bolts.remove(b)

第二に、一度だけ削除bboltsます。それで:

for b in bolts:
  for a in aliens:
    should_remove = False
    for c in aliens:
      if hit:
        should_remove = True
    if should_remove:
      bolts.remove(b)

このコードには他にも問題があると思いますが、それが主な問題の原因です。Martijn の投稿も役立つかもしれません。

于 2013-01-02T17:57:42.320 に答える
0

ボルトにも「ヘルス」を与え、1 に初期化します。次に、1 つのネストされたループを実行してすべてのダメージを計算し、ネストされていない 2 つの別個の「ループ」を実行して「死んだ」ものをすべて削除します。ただし、ループしているリストを変更したくないため、そのようにしないでください。コピーの作成はまだ複雑すぎます。本当にやりたいことは、まだ「生きている」ものだけの新しいリストを直接作成することであり、リスト内包表記を使用して(またはここに示すように を使用してfilter)記述的にそれを行うことができます。

# for example
class Alien:
    # ... other stuff
    def damage(self): self.hp -= 1
    def alive(self): return self.hp > 0

# similarly for Bolt

def collide(an_alien, a_bolt):
    # etc.

def handle_collisions(aliens, bolts):
    for a in aliens:
        for b in bolts:
            if collide(a, b):
                a.damage()
                b.damage()

    return list(filter(Alien.alive, aliens)), list(filter(Bolt.alive, bolts))
于 2013-01-03T00:51:37.167 に答える