迭代列表时如何有效地删除元素?

时间:2012-05-10 22:02:10

标签: python arrays algorithm simulation

我正在使用Python制作一个简单的进化模拟器。

有一个名为engine.All的清单,用于存储单位/动物和食物。我遍历它,如果我遇到一只动物,我会再次遍历它,看看他是否与任何食物碰撞。

如果是这样,那么我增加他的能量,将食物标记为食用,并将其添加到toRemove列表中,我稍后将其用于从engine.All中删除元素。

这是代码,但删除了所有多余的内容:

def remove(l, who): #This should remove all the elements contained in who from the list l
    offset = 0

    for i in who:
        l.pop(i + offset)
        offset -= 1

    return l


for ob in engine.All:
    if ob.skip:
        continue;

    if ob.drawable:
       ob.draw()

    if isinstance(ob, Flatlander): #If it is an animal
        #Do speed stuff
        ob.energy -= decay #Lower its energy

        for i in range(len(engine.All)): #Iterate through the list again
            if collides(ob.pos, ob.r, engine.All[i].pos, engine.All[i].r) and isinstance(engine.All[i], Food) and ob.energy + engine.All[i].r < ob.r**2*3.14 and not engine.All[i].skip: #If it collides with a food piece, the food piece isn't about to be deleted and it can take the energy in (their maximum is defined by their radiuses)
                ob.energy += engine.All[i].r #Increase the his energy
                toRemove.append(i) #Add the food piece to the toRemove list
                engine.All[i].skip = True #Flag it as skipped

        if ob.energy < 0 and not ob.skip: #If its energy is 0 and if it isn't already about to be deleted
            toRemove.append(engine.All.index(ob)) #Add it to the toRemove list
            ob.skip = True #Flag it as skipped


engine.All = remove(engine.All, toRemove) 

我几乎可以肯定这不起作用,而且有更好的方法可以做到这一点。我非常肯定的原因是,有时,我看到屏幕上的东西“闪烁” - 突然消失并再次出现。此外,似乎有“鬼”动物(在代码中称为Flatlanders),我得出结论,因为有时食物片永久消失。

请推荐一种更有效的方法。

3 个答案:

答案 0 :(得分:4)

作为生成器函数执行此操作会更容易,并产生您想要的结果,而不是弹出您不想要的元素。

def valid_engines():
    for ob in engine.All:
        if should_use_engine(ob):
            yield ob

engines_to_use = valid_engines()

should_use_engine()当然会被上面的逻辑所取代,以确定是否包含引擎。

答案 1 :(得分:1)

如果仅为那些需要删除的对象设置skip属性,只需使用它:

import math
import itertools

for ob in engine.All:
    if isinstance(ob, Flatlander):
        ob.energy -= decay

for pair in itertools.combinations(engine.All, 2):
    fooditems = [x for x in pair if isinstance(x, Food) and not x.skip]
    animals = [x for x in pair if isinstance(x, Flatlander) and not x.skip]
    if not (fooditems and animals):
        continue
    animal = animals[0]
    food = fooditems[0]
    if collides(animal.pos, animal.r, food.pos, food.r):
        # This seems an odd calculation to me but I think it follows your code.
        if animal.energy + food.r < animal.r ** 2 * math.pi:
            # eating is feasible; the animal always eats the food if it can
            food.skip = True
            animal.energy += food.r # Not the area?

for ob in engine.All:
    if isinstance(ob, Flatlander) and ob.energy < 0:
        ob.skip = True # dead

# Remove dead things
engine.All = [ob for ob in engine.All if not ob.skip]

# Draw everything (no dead things remain)
for ob in engine.All:
    if ob.drawable:
      ob.draw()

一般来说,我可能更愿意将食品和平板商放在单独的清单中。

有几种技术可以更有效地进行碰撞检测(例如将世界划分为正方形,只检查相同或周围方格中的那些动物),但是如果你只有少数(比如不到几百只)动物/食品这不需要。

答案 2 :(得分:0)

我不熟悉进化游戏,但我想你有某种2D地图(mb 3D)和一些移动的动物吃掉它们碰撞的第一个食物项目?或者它是一种生活游戏?这基本上是一样的:p

如果是这样,我会在广场中添加一个内容字段。当动物移动到正方形时,检查相邻的正方形,如果存在食物项,则只需将该正方形的内容字段设置为Null。 这是在2D游戏中存储每个元素的位置的常见优化,并且如果需要可以进行调整。

您可以使用计时器字段添加已删除项目的列表,以告知每个项目何时应重新生成。 此列表将按重新生成的时间排序,以便您可以轻松检测是否需要重新插入项目。