Multiple Indices |
|
-- Creates a special table that accepts keys that are arrays.. -- You must specified the expected length of this array when -- creating a MultiTable. -- -- Given mt = MultiTable(3), accessing mt[{1,2,3}] semantically -- acts like mt[1][2][3]. -- -- You can also access using a function call syntax. The following -- are equivalent: -- mt[{1,2,3}], mt(1,2,3), mt{1,2,3} -- function MultiDimTable( dimension ) if not dimension then error( "MultiDimTable expected dimension, got nil" ) end local mt = {} -- all the nested access is hidden here local function mt_index(t, k) if type(k) ~= 'table' then return t[{k}] end if dimension ~= #k then error("MultiTable key dimension was " .. #k .. ", expected " .. dimension) end local v = mt for i = 1, #k do v = v[ k[i] ] if not v then return nil end end return v end local function mt_newindex(t, k, v) if type(k) ~= 'table' then t[{k}] = v return end if dimension ~= #k then error("MultiTable key dimension was " .. #k .. ", expected " .. dimension) end local n = mt for i = 1, #k - 1 do local n_old, k_i = n, k[i] n = n[k_i] if not n then n = {} ; n_old[k_i] = n end end n[k[#k]] = v end local function mt_call(t, k, ...) if type(k) == 'table' then return t[k] end return t[{k, ...}] end return setmetatable( {}, { __index = mt_index, __newindex = mt_newindex, __call = mt_call } ) end -- tests do local res, err res, err = pcall( function() MultiDimTable() end ) local mt1 = MultiDimTable(1) mt1[4] = 5 assert( mt1[{4}] == 5 ) assert( mt1[4] == 5 ) assert( mt1{4} == 5 ) assert( mt1(4) == 5 ) mt1['abc'] = '123' assert( mt1[{'abc'}] == '123' ) assert( mt1['abc'] == '123' ) assert( mt1({'abc'}) == '123' ) assert( mt1('abc') == '123' ) res, err = pcall( function() return mt1[{1,2}] end ) assert( not res and string.match(err, 'MultiTable key dimension was 2, expected 1') ) res, err = pcall( function() mt1[{1,2}] = 4 end ) assert( not res and string.match(err, 'MultiTable key dimension was 2, expected 1') ) for i = 1, 100 do local n = {} for j = 1, i do table.insert(n, math.random()) end local val = math.random() local mtn = MultiDimTable(i) mtn[n] = val assert( mtn[n] == val ) assert( mtn(n) == val ) assert( mtn{unpack(n)} == val ) assert( mtn[{unpack(n)}] == val ) end end
do local meta = {} local xlate = {[0] = "x", "y", "z"} function meta:__index(key) if xlate[key] then return rawget(self, xlate[key]) end end function meta:__newindex(key, val) if xlate[key] then rawset(self, xlate[key], val) else rawset(self, key, val) end end function Vector(x, y, z) return setmetatable({x=x, y=y, z=z}, meta) end end a = Vector(1, 2, 3) =a.x 1 =a[0] 1 a[2] = 7 =a.z 7
How does this work?
Firstly data is ONLY stored in x,y,z values in out table created via Vector(1, 2, 3). We use a dummy table and a lookup table to achieve our goal.
TBC....