How to track table delta (solved?)

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

How to track table delta (solved?)

Post by Taehl »

In the process of writing netcode, I realized how incredibly useful it would be if I could somehow know which values in a table have changed. Ideally these would be possible without me having to keep a whole copy of the table and constantly comparing. I thought I could do this with a metatable, however, the __newindex metamethod only gets called when assigning a value to a key which was previously empty - not whenever a value is updated.

Here was my attempt, however, it doesn't work right. It only finds when new values are added to the table, not when any value is updated.

Code: Select all

function deltaTable(watch, delta)
	setmetatable( watch, {
		__newindex = function(t,k,v)
			if v ~= rawget(t,k) then
				rawset(delta,k,v)
			end
			rawset(t,k,v)
		end
	} )
end
Any suggestions?
Last edited by Taehl on Sun Feb 17, 2013 6:50 am, edited 1 time in total.
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: How to track table delta?

Post by Robin »

Use a proxy table: the proxy remains empty and it only sets values in the other table.

You also need an __index for the proxy that takes values from that other table.
Help us help you: attach a .love.
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: How to track table delta?

Post by Taehl »

That's a good idea, Robin. Thanks. I ran with it in what's probably not the direction you had in mind, but what do you think of this?

Code: Select all

-- only downside: won't work with pairs()
function makeDeltaTable(t)
	t = t or {}
	t.__delta = {}
	t.__index = function(t,k) return getmetatable(t)[k] end
	t.__newindex = function(t,k,v)
		if v ~= getmetatable(t)[k] then getmetatable(t).__delta[k] = v end
		getmetatable(t)[k] = v
	end
	t.__unm = function(t) return getmetatable(t).__delta end	-- -table returns full delta table
	t.__sub = function(t,k) return getmetatable(t).__delta[k] end -- table-key returns delta value
	return setmetatable( {}, t )
end

-- here are some tests:
t = makeDeltaTable( {x=10,y=10} )
t.x,t.y = 10,25		-- only t.y changed
print("delta t.x = "..tostring(t-"x"))	-- nil, because x didn't change
for k,v in pairs(-t) do print("delta t."..k.." = "..v) end	-- only shows delta t.y = 25
t.__delta = {}						-- empty the delta table
print("t.__delta={}. Now delta t.y = "..tostring(t-"y"))	-- nil
print("t.x, t.y = "..t.x..", "..t.y)	-- 10, 25
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: How to track table delta (solved?)

Post by Robin »

What I think of it depends on whether it works for you. If it does, great!
Help us help you: attach a .love.
spectralcanine
Citizen
Posts: 65
Joined: Sat Dec 22, 2012 8:17 am

Re: How to track table delta (solved?)

Post by spectralcanine »

You should probably make __delta private, but whatever fits your needs.

As to pairs, you can create your own iterator :)
User avatar
Xgoff
Party member
Posts: 211
Joined: Fri Nov 19, 2010 4:20 am

Re: How to track table delta?

Post by Xgoff »

Taehl wrote:That's a good idea, Robin. Thanks. I ran with it in what's probably not the direction you had in mind, but what do you think of this?

Code: Select all

-- only downside: won't work with pairs()
function makeDeltaTable(t)
	t = t or {}
	t.__delta = {}
	t.__index = function(t,k) return getmetatable(t)[k] end
	t.__newindex = function(t,k,v)
		if v ~= getmetatable(t)[k] then getmetatable(t).__delta[k] = v end
		getmetatable(t)[k] = v
	end
	t.__unm = function(t) return getmetatable(t).__delta end	-- -table returns full delta table
	t.__sub = function(t,k) return getmetatable(t).__delta[k] end -- table-key returns delta value
	return setmetatable( {}, t )
end

-- here are some tests:
t = makeDeltaTable( {x=10,y=10} )
t.x,t.y = 10,25		-- only t.y changed
print("delta t.x = "..tostring(t-"x"))	-- nil, because x didn't change
for k,v in pairs(-t) do print("delta t."..k.." = "..v) end	-- only shows delta t.y = 25
t.__delta = {}						-- empty the delta table
print("t.__delta={}. Now delta t.y = "..tostring(t-"y"))	-- nil
print("t.x, t.y = "..t.x..", "..t.y)	-- 10, 25
that could be prematurely optimized somewhat

Code: Select all

-- only downside: won't work with pairs()
function makeDeltaTable(t)
   t = t or {}
   t.__delta = {}
   t.__index = t
   t.__newindex = function(_,k,v)
      if v ~= t[k] then t.__delta[k] = v end
      t[k] = v
   end
   t.__unm = function(_) return t.__delta end   -- -table returns full delta table
   t.__sub = function(_,k) return t.__delta[k] end -- table-key returns delta value
   return setmetatable( {}, t )
end
you could even go so far as making the delta table a local, in which case it'd automatically become private simply by being an upvalue of some of the metamethods (it'd also be slightly faster too)
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 6 guests