ID:179171
 
I have a loop that does something like this:

for(var/obj/O in list_of_obj)
var/list/objects = list()
O.Find_certain_objs(objects) // objects will now contain O AND may contain other obj's further down in list_of_obj
for(var/obj/OO in objects)
list_of_obj -= OO


I used to have "del(OO)" instead of list_of_obj -= OO, and this seemed to work fine, but for other reasons I now need to keep OO alive.

The problem is that the outer loop STILL LOOPS on the future items in "objects" (ie. OO != O) even though they were removed from the list. I read somewhere that this could be a problem if you loop on world (world.contents), but it shouldn't occur if you are looping on anything smaller. Am I missing something here?
Untested workaround that wasn't asked for:

Something to the point of...

var
list/objects = list()
obj/O

// Find the objects
for(O in list_of_obj)
// Assuming Find_certain_objs() doesn't ruin input list
// Note: An obj could be put in objects multiple times
O.Find_certain_objs(objects)

// Remove the objects
// Choose the shortest list to loop through
if (length(objects) < length(list_of_obj))
for(O in objects)
// This won't freak out if O has already been removed
list_of_obj -= O
else
for(O in list_of_obj)
if (O in objects)
list_of_obj -= O
In response to ACWraith
I appreciate the help, but I don't think this will work for me. I need "objects" to be specific to O for other reasons not shown in my example. I'll fill in some specifics to help provide context. Suppose list_of_obj contains a list of people who have come to occupy roads during the game. There will be separate roads on the map, but it is possible for multiple people to be on the same road. I need "Find_certain_objs" to give me a list of people ON THE SAME ROAD as O (including O). With the code below, objects would definitely contain all the people in the world, and multiple copies of those people if they shared roads.

I loop on people in order to find and score a particular road, but I need to remove ALL the people on that road before continuing the loop (so I don't score the same road again).

I hope this makes sense...

Thanks.


ACWraith wrote:
Untested workaround that wasn't asked for:

Something to the point of...

> var
> list/objects = list()
> obj/O
>
> // Find the objects
> for(O in list_of_obj)
> // Assuming Find_certain_objs() doesn't ruin input list
> // Note: An obj could be put in objects multiple times
> O.Find_certain_objs(objects)
>
> // Remove the objects
> // Choose the shortest list to loop through
> if (length(objects) < length(list_of_obj))
> for(O in objects)
> // This won't freak out if O has already been removed
> list_of_obj -= O
> else
> for(O in list_of_obj)
> if (O in objects)
> list_of_obj -= O
>
In response to Dramstud
Having another kind of object around makes it easier in theory. If it all depends on the roads, you could do something to the point of:

// Assuming roads is roads in the world or a subset
// Assuming people is people in the world or subset

var
obj/Road/road
list/roadPeople

for(road in roads)
roadPeople = road.GetPeople()
people -= roadPeople


Mind you, I realize my workaround doesn't answer the original question. I'm interested in what is happening behind the scenes too.
In response to ACWraith
Yeah, it would probably be a lot easier if I had road objects, but unfortunately I don't. I have tile objects, and a tile may contain a road (among other things), but it is only represented by a 4-bit flag which indicates the presence of a road heading in each of the four cardinal directions.

What I'm really looking for here is understanding of how lists are handled during looping. Why is it ok to delete a future object in the list, but not to simply remove the object from the list?? Does DM make a copy of the list when it starts the loop so that the list can't be tampered with? In this case it finds a null object (if it was deleted earlier) and could crash the proc, but I think my proc is set up such that it simply moves on to the next object.

ACWraith wrote:
Having another kind of object around makes it easier in theory. If it all depends on the roads, you could do something to the point of:

> // Assuming roads is roads in the world or a subset
> // Assuming people is people in the world or subset
>
> var
> obj/Road/road
> list/roadPeople
>
> for(road in roads)
> roadPeople = road.GetPeople()
> people -= roadPeople
>

Mind you, I realize my workaround doesn't answer the original question. I'm interested in what is happening behind the scenes too.
In response to Dramstud
dramstud wrote:
What I'm really looking for here is understanding of how lists are handled during looping. Why is it ok to delete a future object in the list, but not to simply remove the object from the list?? Does DM make a copy of the list when it starts the loop so that the list can't be tampered with? In this case it finds a null object (if it was deleted earlier) and could crash the proc, but I think my proc is set up such that it simply moves on to the next object.

In a for(item in list) loop, a copy is indeed made before the loop begins--so you can remove an item from the list while you loop and it's okay. (I asked that myself a few months ago and was surprised it worked that way. I've taken advantage of that helpful behavior since then.) When you call del(O) on an object that's in the list, it's still deleted, but it has to be replaced with null. So when you get to that item, it will be null.

If you prefer to avoid this copying behavior, you can use an index variable and access the items directly:
for(var/i in 1 to thelist.len)
item=thelist[i]
...

That works, because thelist.len is allowed to change during the loop. I believe the main reason the copying behavior in the other type of loop works is because if you call for(thing in view()) or such, that function that supplies the list would have to be called over and over--so it's easier to call it once and make a copy.

Lummox JR
In response to Lummox JR
Lummox JR wrote:
If you prefer to avoid this copying behavior, you can use an index variable and access the items directly:
for(var/i in 1 to thelist.len)
> item=thelist[i]
> ...

That works, because thelist.len is allowed to change during the loop.

This method would solve my problem. I gave it a test run, and the scoring was done properly, but I got this error:

runtime error: list index out of bounds

Any advice on getting rid of the error?
I was using "thelist -= item" ; should I be removing items a different way to avoid runtime errors?

thanks.