Lua:how to create a custom method on all tables - lua

I'd like to create a custom contains method on Lua's table data structure that would check for the existence of a key. Usage would look something like this:
mytable = {}
table.insert(mytable, 'key1')
print(mytable.contains('key1'))
Thanks.

In Lua you cannot change ALL tables at once. You can do this with simpler types, like numbers, strings, functions, where you can modify their metatable and add a method to all strings, all functions, etc. This is already done in Lua 5.1 for strings, this is why you can do this:
local s = "<Hello world!>"
print(s:sub(2, -2)) -- Hello world!
Tables and userdata have metatables for each instance. If you want to create a table with a custom method already present, a simple table constructor will not do. However, using Lua's syntax sugar, you can do something like this:
local mytable = T{}
mytable:insert('val1')
print(mytable:findvalue('val1'))
In order to achieve this, you have to write the following prior to using T:
local table_meta = { __index = table }
function T(t)
-- returns the table passed as parameter or a new table
-- with custom metatable already set to resolve methods in `table`
return setmetatable(t or {}, table_meta)
end
function table.findvalue(tab, val)
for k,v in pairs(tab) do
-- this will return the key under which the value is stored
-- which can be used as a boolean expression to determine if
-- the value is contained in the table
if v == val then return k end
end
-- implicit return nil here, nothing is found
end
local t = T{key1='hello', key2='world'}
t:insert('foo')
t:insert('bar')
print(t:findvalue('world'), t:findvalue('bar'), t:findvalue('xxx'))
if not t:findvalue('xxx') then
print('xxx is not there!')
end
--> key2 2
--> xxx is not there!

Related

Strange bug with table in lua

I'm adding a string to a table in lua. When I use the table in a function the original table is getting altered. I'm only a beginner but I thought that the function could not do that because it is outside of it's scope. Is there something obvious I'm missing?
local testTable= {}
testTable.name = {}
testTable.name[1] = "Jon"
print(testTable.name[1])
local function testFunc(a)
a.name[1] = "Bob"
end
local newTable = testTable
testFunc(newTable)
print(testTable.name[1])
I expected the output to be:
Jon
Jon
The actual output is:
Jon
Bob
How can the testFunc change the testTable?
You assign testTable's address to newTable, so testTable and newTable point to the same table.
If you want to output be
Jon
Jon
You should copy the table when you assign newTable.
You can copy the table like this function:
function table.copy(old)
local new = {}
for k, v in pairs(old) do
new[k] = v
end
return new
end
When I use the table in a function the original table is getting altered. ... I thought that the function could not do that because it is outside of it's scope.
Local variables have their own scope, but tables do not. Two things to remember:
Variables store references, not values. (This only makes a difference for mutable values.)
Tables are mutable, i.e., they can be changed internally.
Breaking it down:
local newTable = testTable
In this line, you're assigning one variable to another, so both variables refer to the same table.
We mutate a table by assigning to an index within that table, so testFunc alters whatever a (actually a.name) refers to. This is handy, because it allows us to write functions that mutate tables that we pass as arguments.
The following function does nothing, like you would expect, because it assigns a new table to the bare name a (which happens to be a local variable):
local function doNothing(a)
a = {name = {'Bob'}}
end

Lua metamethod that fires when removing an element from a table

I am looking for a metamethod (or a workaround) that fires when removing an element from a lua table similar to the __newindex metamethod.
Ideally it would work something like the following:
local mytable = {}
local mt = {
__newindex = function(t,k,v)
rawset(t,k,v)
-- some other functionality
end,
-- This does not exist
__remove = function(t,k)
--some functionality
end
}
setmetatable(mytable,mt)
-- __newindex fires
mytable["key"] = value
-- __remove fires
mytable["key"] = nil
I have tried working with the __gc metamethod but that is not usable in this implementation due to the fact that the metamethod only triggers when the garbage collection cycle happens. I have no control over the garbage collection because the table (with the metamethods) is passed to a different script.
Possible workaround - do not store actual data within table.
Let your mytable act as a proxy, and store actual values in some shadow table. It might be allocated along with mytable, or data can be stored directly in metatable (so metatable must be created per mytable instance).
Here's example (easily broken by writing data under metamethods' name keys, but you get an idea), data will be stored within metatable:
http://ideone.com/eCOal3
local mytable = {}
local mt = {}
function mt.__newindex(t,k,new_value)
local previous_value = mt[k]
rawset(mt,k,new_value)
if previous_value and new_value == nil then
print "__remove() triggered"
end
end
mt.__index = mt
setmetatable(mytable, mt)
mytable.key = 123
print(mytable.key)
mytable.key = nil
print(mytable.key)
As assigning nil fires not metamethod at all, you will have to resort to an explicit removal function that does whatever you wanted the metamethod to do and then assign nil to the table entry.

Use table.insert with dynamic variable names

I got a question on how to use dynamic variable names with the table.insert function in Lua.
I want to create some tables with dynamic variable names and afterwards access those tables using table.insert function to fill them with values, however, I am unclear on how to access the newly created table inside of the table.insert function.
My code so far looks like this:
local attributeNames = {"attribute1","attribute2","attribute3"}
local attributes = {}
for k, v in pairs(attributeNames) do
// If attribute name equals name of type then create a new table with
that type name
if(string.match(v, type)) then
attributes[v] = {}
currentAttribute = v
break
end
end
// Insert values into the table with that type name, here I do not get how to call
the table of name "attribute1" for example to fill it with values
table.insert (attributes[currentAttribute], values)
Any help is highly welcome! :)

Confusion of using "." notation with __index and namespace in Lua

I am confused of the following two syntaxes using "."
From what I understand, __index is called when a key doesn't exist in a table but exists in its metatable. So why does the list table call __index and then assign itself to list.__index?
list = {}
list.__index = list
setmetatable(list, { __call = function(_, ...)
local t = setmetatable({length = 0}, list)
for _, v in ipairs{...} do t:push(v) end
return t
end })
function list:push(t)
if self.last then
self.last._next = t
t._prev = self.last
self.last = t
else
self.first = t
self.last = t
end
self.length = self.length + 1
end
.
.
.
local l = list({ 2 }, {3}, {4}, { 5 })
Does Window.mt simply create a table? Why do we need Window = {} as a namespace here?
Window = {} -- create a namespace
Window.mt = {} -- create a metatable
Window.prototype = {x=0, y=0, width=100, height=100, }
function Window.new (o)
setmetatable(o, Window.mt)
return o
end
Window.mt.__index = function (table, key)
return Window.prototype[key]
end
w = Window.new{x=10, y=20}
print(w.width) --> 100
So why does the list table call __index and then assign itself to list.__index?
Nowhere in your code does the list table call __index. The assignment part however is a common Lua idiom (aka. hack) to save some memory. Conceptually there are 4 different kinds of tables involved:
list objects (the tables created via {length=0} in your code)
a metatable (containing an __index field) that modifies the behavior of list objects when you try to access non-existing fields in the object
the list class, which holds all the methods for list objects (like the push method), and also serves as a constructor for list objects
a metatable (containing a __call field) for the list class, so that you can call the list table as if it were a function
As metatable fields always start with two underscores (__), and normal methods usually don't, you can put metatable fields and normal methods side by side into a single table without conflict. And this is what happened here. The list class table also serves as metatable for list objects. So using this trick you can save the memory you would normally need for the separate metatable (the size in bytes for Lua 5.2 on an x86-64 Linux is shown in square brackets in the table title bars, btw.):
Does Window.mt simply create a table?
No, {} creates a table. However, this new table is saved under key "mt" in the Window table, probably to give users of this Window "class" direct access to the metatable that is used for window objects. Given only the code you showed this is not strictly necessary, and you could have used a local variable instead.
Why do we need Window = {} as a namespace here?
In principle, you could store Window.mt, Window.new, and Window.prototype separately, but that would get cumbersome if you have multiple "classes" like Window. This way you can avoid name clashes, and using the Window "class" looks nicer.
Another reason might be that require can only return a single value from a module definition, and if you want to export multiple values (like new, mt, and prototype) from a module, you need a table to wrap them together (or use global variables, but that is considered bad style).

using type() function to see if current string exist as table

Is it possible to see if a string is the same as the name of a table?
For example:
I know that a table called 'os' exists, and I have a string "os".
Is there then a way to do this:
x="os"
if type(x)=="table" then
print("hurra, the string is a table")
end
Of course this example wont work like I want it to because
type(x)
will just return "string".
The reason why I want to do this, is just because I wanted to list all existing Lua tables, so I made this piece of code:
alphabetStart=97
alphabetEnd=122
function findAllTables(currString, length)
if type(allTables)=="nil" then
allTables={}
end
if type(currString)=="table" then
allTables[#allTables+1]=currString
end
if #currString < length then
for i=alphabetStart, alphabetEnd do
findAllTables(currString..string.char(i), length)
end
end
end
findAllTables("", 2)
for i in pairs(allTables) do
print(i)
end
I wouldn't be surprised if there is an easier method to list all existing tables, I'm just doing this for fun in my progress of learning Lua.
If you want to iterate over all global variables, you can use a for loop to iterate over the special _G table which stores them:
for key, value in pairs(_G) do
print(key, value)
end
key will hold the variable name. You can use type(value) to check if the variable is a table.
To answer your original question, you can get a global variable by name with _G[varName]. So type(_G["os"]) will give "table".
interjay gave the best way to actually do it. If you're interested, though, info on your original question can be found in the lua manual. Basically, you want:
mystr = "os"
f = loadstring("return " .. mystr);
print(type(f()))
loadstring creates a function containing the code in the string. Running f() executes that function, which in this case just returns whatever was in the string mystr.

Resources