I’m a very object-oriented kind of guy. Today as my friend and I were pairing on this game, we were writing a loop that processed all kinds of objects. We were going to kill them under certain circumstances. He suggested that in our higher-level loop we should just jam a dead property into whatever object it was:
obj.dead = true
As an OO guy, I feel that’s obscene. If objects can be dead, then they should all have a property dead and be aware of it. With accessors if you worked that way (I don’t) and so on.
As any kind of guy I note that if you go setting a key/value pair into all the objects that pass by, you’re at risk of using a key that that object already uses. The Zombie object might already have self.dead = 0.5 or something. So it seems to me to be a horrible hack.
And yet, if you knew the key was unique it has a certain terrible elegance.
And yet, it is even worse than a violation of the Law of Demeter.
What do you think? Good idea? Bad idea? When and why?
@RonJeffries I don’t worry about if something is right or wrong, I just worry about wether it works or not. But then I write sloppy code and when it works the way I want it to, I clean it up and remove what I really don’t need.
What you’ve described is my standard method for removing entities.
Update loop:
for i=#entities, 1, -1 do
local v = entities[i]
if v.kill then
table.remove(entities, i)
else
v:update()
end
end
forgetting that kill or dead or whatever is a variable that’s been taken and reusing it would be disasterous, but then so would trying to reuse updatedraw etc. In Lua you just have to be careful with your namespaces, there’s no way to lock down eg, the variables that hold a class’s methods
@yojimbo2000 why doesn’t that code skip updating number 5 if 4 dies? It seems the table.remove will move 5 up to 4, and then the loop will go to i=5, skipping the former #5.
Actually you don’t have to go backwards through a table to eliminate skipping an entry. Here’s an example that shows an entry being skipped and also not skipped. I created a 9 entry table with the values 1 thru 9. Using pairs, I print the values for “a” the entry number, “b” the entry value, and tab[a] also the entry value. When this is run, I remove table entry 5 when “a” is 5. Looking at the “b” values, you can see that table value 6 is skipped because it was shifted down to fill table entry 5 that was deleted. You can also see that entry “b=5” was still valid after it was removed because the values were populated in the variables “a” and “b” when the “for” loop was processed. But if you look at the “tab[a]=” values, only table entry 5 is skipped because “tab[a]” is populated after the table.remove is run. So you can go thru a table in a forward fashion without skipping an entry.
function setup()
tab={}
for z=1,9 do
table.insert(tab,z)
end
for a,b in pairs(tab) do
if a==5 then
table.remove(tab,5) -- remove entry 5
print("remove entry tab[5]")
end
print("a="..a,"b="..b,"tab[a]="..tab[a])
end
end
@dave1707 I guess am missing your point. The table.remove causes an item (b=6) to be skipped in the print and only 8 iterations to be printed instead of the “expected” 9. what am i missing?
Anyway my question is a question of style: it seems to me that pushing a k,v pair into an object of unknown type is at best iffy. I was interested in whether folks agreed that it’s a dangerous thing to do, or not.
@RonJeffries What I was trying to point out is that the variables a and b in the for loop get their values at the start of each iteration of the loop. No matter what you do in the for loop with the table, a and b still have the values they started with. When I reach the 5th entry in the table, I remove it. When I do the print after the remove statement, b still prints 5 even though table entry 5 was removed. But when I print tab[a] when a is still 5, it prints the correct entry value of 6 because table.remove moved everything down 1 entry in the table when it removed entry 5. On the next iteration of the for loop when the value of a is 6, b has a value of 7 because the entries above 5 were shifted down. The reason 8 iterations are printed is because I started with 9 entries, deleted the 5th entry, so there were only 8 entries left in the table. So if you want to iterate thru a table from start to finish and you remove entries as you go, it’s best to use tab[] to get the values in the table instead of using the b variable in the for loop. So if you run my example, you’ll see the the variable b prints 1 thru 5 then 7 thru 9, skipping 6 which is wrong. But tab[a] prints the correct entries, 1 thru 4, 6 thru 9, skipping 5 because it was removed. Hope I explained it well enough.
@RonJeffries That’s what I was saying. If you use the variable b to get the table value, it doesn’t skip 5, it skips 6 which is wrong because 5 was deleted. But if you use the variable tab[a] to get the table value, it skips 5 because 5 was deleted and it picks up with 6 which is correct.
@RonJeffries - I think you’ll find if you move the print statement ahead of the deletion, you’ll see 9 iterations
EDIT - no, I’m wrong. Dave, if you alter your code to delete both item 5 and item 6, you’ll see it actually removes items 5 and 7. The variable a does renumber after a deletion, so deleting forwards doesn’t work.
Point is, table.remove removes an item and moves all the others up, because no holes in indexed tables. so if you’re counting upward, you’re going to miss one in the X of a for that goes
for i,X in table do
Isn’t this a case for a parent and child object, the parent having the “kill” property, and all the child classes inheriting that property (plus any others you want all objects to share)?
You probably know this, but Lua/Codea does provide for inheritance