Style Question - setting a value into another guy's table

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?

I presume you don’t just want to say

obj=nil

You could of course use a unique property

obj.hasJoinedTheChoirInvisible=true

However, what I think is nice about Lua is that tables can implicitly have a property even when they don’t . By that, I mean that this line

if not obj.IsDead then 

will act on all objects that either have a property IsDead=false, or which don’t have that property at all.

So you can just add a property to certain objects, and test all objects for that property even if they don’t have it.

@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 update draw 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.

That’s why you have to go through the table backwards. It’s the only way to remove items without skipping any or crashing out the end of the table

ah yes i didn’t notice the backwards, thanks

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.

What would be your preferred way of removing the objects?

@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.

It doesn’t skip #5. It skips #6, going right on to 7

@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.

ISTM you only get 8 iterations if you delete during the loop, not 9 as expected

@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

and that’s why you should go backwards :smiley:

yes. way off topic, but true

@RonJeffries - to get back to your question

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

A = class()
B = class(A)  --inherits class A