implementing fallback/default-getter in Lua's tables - lua

Is there a way to implement a mechanism similar to python's __getitem__?
for instance, having the following:
local t1 = {a=1, b=2, c=3, d=4}
if in code, t1.e will be called, then I wish to have something else returned rather than nil

You can use setmetatable and the __index metamethod:
local t1 = {a = 1, b = 2, c = 3, d = 4}
setmetatable(t1, {
__index = function(table, key)
return "something"
end
})
print(t1.hi) -- prints "something"
Note that this will not be called when you do t.nonexistant = something. For that, you need the __newindex metamethod:
local t1 = {a = 1, b = 2, c = 3, d = 4}
setmetatable(t1, {
__index = function(table, key)
return "something"
end,
__newindex = function(table, key, value)
rawset(table, tostring(key) .. '_nope', value)
end
})
print(t1.hi) -- prints "something"
t1.hi = 'asdf'
print(t1.hi) -- prints "something"
print(t1.hi_nope) -- prints "asdf"

Related

How to combine two lua tables and maintain their ordering?

Given
local t1 = {
["foo"] = "val1",
["bar"] = "val2",
["baz"] = "val3",
}
local t2 = {
["foo1"] = "val4",
["bar1"] = "val5",
["baz1"] = "val6",
}
Id like to get result
local t3 = {
["foo"] = "val1",
["bar"] = "val2",
["baz"] = "val3",
["foo1"] = "val4",
["bar1"] = "val5",
["baz1"] = "val6",
}
I have been attempting various ways for around a day now, using other questions provided here, and still am unsure of where things are going wrong or how to handle it. The tables vs arrays in lua is a bit hard to grasp. Thanks for any help :D
AFAIK, if the hash part of a table is used, it does not preserve the order. There is a very simple way to highlight this.
t1 = {
["foo"] = "val1",
["bar"] = "val2",
["baz"] = "val3",
}
for k,v in pairs(t1) do
print(k, v)
end
On my computer, the order is not preserved:
foo val1
baz val3
bar val2
If the order is important, it is required to use an array.
One (convoluted) way to do it would be the following code. Obviously, I am not aware of the given requirements, so this code might not be the most straight-forward.
t1 = {
{ foo = "val1" }, -- Element t1[1]
{ bar = "val2" }, -- Element t1[2]
{ baz = "val3" } -- Element t1[3]
}
t2 = {
{ foo1 = "val4" }, -- Element t2[1]
{ bar1 = "val5" }, -- Element t2[2]
{ baz1 = "val6" } -- Element t2[3]
}
function merge (t1, t2)
local new_table = {}
-- Copy all the items from the first table
for index = 1, #t1 do
new_table[#new_table+1] = t1[index]
end
-- Copy all the items from the second table
for index = 1, #t2 do
new_table[#new_table+1] = t2[index]
end
-- return the new table
return new_table
end
for k,v in pairs(merge(t1,t2)) do
local subtable = v
for k,v in pairs(subtable) do
print(k,v)
end
end
This would print the following:
foo val1
bar val2
baz val3
foo1 val4
bar1 val5
baz1 val6

Setting multiplication operator through metatable in specific environment in Lua

local names = setmetatable({},
{__mul = function(a,b) return a|b end}
)
names={i=0,j=1}
tr=load("return i*j",nil,"t",names)()
print(tr)
It prints tr as 0. The expected answer is 1 as 0|1 results to 1. Where is the code wrong?
Try:
local mt_obj = {
__tostring = function(o) return tostring(o[1]) end,
}
local function get(o)
if type(o) == "table" then return o[1] else return o end
end
local function new(v)
return setmetatable({v}, mt_obj)
end
function mt_obj.__mul(a,b)
return new(get(a)|get(b))
end
local mt_env = {
__index = function(t,k) return new(t.variables[k]) end,
__newindex = function(t,k,v) t.variables[k] = v end,
}
local names = {i=0,j=1}
local env = setmetatable({variables = names}, mt_env)
tr=get(load("return i*j*8",nil,"t",env)())
print(tr)

How do we change the way print displays a table

Assuming I have a piece of code such as the following
aTable = {aValue=1}
aTable_mt = {}
print(aTable)
What must I do to make Lua print something like aTable current aValue = 1 as opposed to table: 0x01ab1d2.
So far I've tried setting the __tostring metamethod but that doesn't seem to be invoked by print. Is there some metamethod I've been missing or does the answer have nothing to do with metamethods?
__tostring works:
aTable = {aValue=1}
local mt = {__tostring = function(t)
local result = ''
for k, v in pairs(t) do
result = result .. tostring(k) .. ' ' .. tostring(v) .. ''
end
return result
end}
setmetatable(aTable, mt)
print(aTable)
This prints aValue 1 (with one extra whitespace, remove it in real code). The aTable part is not available, because aTable is a variable that references the table, not the content of the table itself.
I'm not sure how you set the metamethod, but the following code prints "stringified" for me:
local aTable = {a = 1, b = 2}
setmetatable(aTable, {__tostring = function() return "stringified" end})
print(aTable)
If you want lua to generally print all tables human readable, you could
hook up/overwrite the print function:
local orig_print = print
print = function(...)
local args = {...}
for i,arg in ipairs(args) do
if type(arg) == 'table' then
args[i] = serialize(arg)
end
end
orig_print(table.unpack(args))
end
serialize could be serpent or some other lib from here
Note that this must be done before any other module/script is loaded.

How can I implement a read-only table in lua?

I wrote an example.
function readOnly(t)
local newTable = {}
local metaTable = {}
metaTable.__index = t
metaTable.__newindex = function(tbl, key, value) error("Data cannot be changed!") end
setmetatable(newTable, metaTable)
return newTable
end
local tbl = {
sex = {
male = 1,
female = 1,
},
identity = {
police = 1,
student = 2,
doctor = {
physician = 1,
oculist = 2,
}
}
}
local hold = readOnly(tbl)
print(hold.sex)
hold.sex = 2 --error
It means that I can give access to the field of the table "tbl" but at the same time, I cannot change the value related to the field.
Now, the problem is that I wanna let all the nested tables own this read-only
property.How can I improve the "readOnly" method?
You just have to apply your readOnly function recursively to the inner table fields as well. You can do so on-access in the __index metamethod. You should also cache the readonly proxy tables that you create, otherwise any read access to inner tables (e.g. hold.sex) will create a new proxy table.
-- remember mappings from original table to proxy table
local proxies = setmetatable( {}, { __mode = "k" } )
function readOnly( t )
if type( t ) == "table" then
-- check whether we already have a readonly proxy for this table
local p = proxies[ t ]
if not p then
-- create new proxy table for t
p = setmetatable( {}, {
__index = function( _, k )
-- apply `readonly` recursively to field `t[k]`
return readOnly( t[ k ] )
end,
__newindex = function()
error( "table is readonly", 2 )
end,
} )
proxies[ t ] = p
end
return p
else
-- non-tables are returned as is
return t
end
end

infinite recursion in __index metamethod

As I understand lua don't call __index unless the key wasn't found in the table so I have that code and it suffers from infinite recursion in the __index part which I don't get as both values used inside the __index function already exist in the table!?
This is basically a test script for trying to save the size of the table in a memory to retreive when # is called
do
local lenKey,originalKey = {},{}
fastKey = {}
fastKey.__len = function(t) return t[lenKey] end
fastKey.__index = function (t,k)
t[lenKey] = t[lenKey] +1
return t[oroginalKey][k]
end
fastKey.__newindex = function(t,k,v) t[originalKey][k] = v end
fastKey.__pairs = function ()
return function (t, k)
return next(t[oroginalKey], k)
end
end
function fastLen(t)
local proxy = {}
local c = 0
for _ in pairs(t) do
c=c+1
end
proxy[lenKey] = c
proxy[originalKey] = t
setmetatable(proxy,fastKey)
return proxy
end
end
n = fastLen{1,2,3,x=5}
--n:insert(1) -- here the __index is called and gets stackoverflow
print(#n)
You've got two typos in there: both the __index and __pairs functions contain oroginalKey instead of originalKey.

Resources