Lua: Variable assignment via table - lua

currently I am trying to export and re-import some variables in LUA.
Imagine a table AllGlobals. The table contains all global variables used in my script, while key contains the variable name and value - surprise - contains the variable value.
What my script should do now is a reassignment of the variables. I started thinking about the following solution, which obviously isn't working:
1 function InsertGlobals()
2 for key,value in pairs(AllGlobals) do
3 key = value
4 end
5 end
Well: Does anybody know a solution for assigning the value in line 3 to the variable name contained in key, not inserting it into the variable key itself?

Solution to my own problem:
function InsertGlobals()
for key,value in pairs(AllGlobals) do
_G[key]=value
end
end

You are only doing a shallow iteration - you don't transverse over tables in tables. For example, the table input wouldn't put something like examp.newfunc() into the _G as _G.examp.newfunc(). Also, it isn't wise to duplicate references of _G, package, and _ENV unless you really know what you are doing, meaning having a purpose.
function InsertGlobals(input)
for key, value in pairs(input) do
if(key ~= "_G" or key ~= "package" or key ~= "_ENV")then
_G[key] = value
end
end
end
To fix the shallow iteration, all you need to do is a little recursion. I am sure you won't blow your stack with this, but if you leave _G, package, and _ENV then you definitely will.
function InsertGlobals(input)
for key, value in pairs(input) do
if(key ~= "_G" or key ~= "package" or key ~= "_ENV")then
if(type(value) ~= "table")then
_G[key] = value
else
InsertGlobals(value)
end
end
end
end

Related

How can i use table.insert without it inserting the index?

local t={}
for i,v in pairs(game.Players:GetPlayers()) do
if v~=game.Players.LocalPlayer then
table.insert(t,v.Name)
end
end
if i do table.foreach(t,print) it also shows the index and it causes problems when i want to select a random player. The error usually tends to be something like "1 is not a valid member of workspace"
Your problem is not that table.insert adds an index, but that you use it instead of the value you want do use.
table.insert inserts an element to a list. If no index is given the value is added to the end of the list.
So there is no way to use table.insert without "inserting the index". Actually there is no way at all to add an element to a Lua table without providing a key. A Lua table is nothing but a set of key value pairs.
table.foreach calls a function for each key value pair.
You should rather use table.foreachi btw.
So table.foreachi(t, print) is equivalent to
print(1, t[1])
print(2, t[2])
...
If you just want to print the values:
for i,v in ipairs(t) do print(v) end
or for older Lua versions
table.foreachi(t, function(i,v) print(v) end)
Alternatively you run a numeric for loop:
for i = 1, t.n do print(t[i]) end
or
for i = 1, #t do print(t[i]) end
table.foreach(t,f) applies the function f to each key-value pair of t as is written. Also, since Lua 5.1 the function is deprecated.
If you want simply to print all values, do
for _, name in ipairs (t) do
print (name)
end
or
print (table.concat (t, ', '))
If you want a random name from t:
local random_name = t [math.random (#t)]

Lua: When and how to write tables to _G

I am learning Lua from a book, and I am NOT a programmer. I am trying to save a table of data to a file using the following functions (that were copied directly from the book), but the function is getting an error when trying to get a string from _G[resTable]. Why?
function readFromFile(filename,resTable)
local hfile = io.open(filename)
if hfile == nil then return end
local results = {} -why is this table here?
local a = 1
for line in hfile:lines() do-- debug shows this loop doesn't run (no lines in hfile?)
_G[resTable[a]] = line
a = a + 1
end
end
function writeToFile(filename, resTable)
local hfile = io.open(filename, "w")
if hfile == nil then return end
local i
for i=1, #resTable do
hfile:write(_G[resTable[i]])--bad argument #1 to 'write' (string expected, got nil)
end
end
'writeToFile" gets an error when trying to :write to _G[resTable[i]]. In the two previous functions listed here, I don't understand why they are referencing _G[resTable[i]] since I don't see any code that is writing to _G.
So here is the order of execution:
local aryTable = {
"Score",
"Lives",
"Health",
}
readFromFile("datafile", aryTable)
writeToFile("datafile", aryTable)
and I get an error:
bad argument #1 to 'write' (string expected, got nil)
stack traceback:
[C]: in function 'write'
test.lua:45: in function 'writeToFile'
test.lua:82: in main chunk
Apparently the author has implemented a way of saving a list of global variables to file and restore them.
The function writeToFile expects a filename and a list of global variables names (resTable). Then it opens a the filename for writing and iterates over the provided names:
for i=1, #resTable do
hfile:write(_G[resTable[i]])
end
in this loop resTable[i] is the i-th name and _G[resTable[i]] is the corresponding value, taken from the table _G, which stores all the globals. If a global with that name is not defined, _G[resTable[i]] will return nil, which is the cause of the failure you experienced. Thus you must provide a resTable that is filled with names of existing globals to avoid this error.
Apart from this, the serialization strategy of the author is really naive, since it handles only variables with string values. In fact by saving the variables to file like that the type information is lost, thus a variable having the value "100" (a string) and another with value 100 (a number) will be stored the same on disk.
The problem is evident analyzing the readFromFile function. After opening the file for reading, it scans it line by line, creating a new variable for each name mentioned in its resTable list:
local a = 1
for line in hfile:lines() do
_G[resTable[a]] = line
a = a + 1
end
the problem is manyfold:
the loop variable line will always have a string value, thus the recreated globals will be all strings, even if they were numbers originally;
it assumes that the variables are recreated in the same order, thus you must provide the same names in resTable you used when you saved the file;
it assumes that the values are stored one per line, but this is a false assumption, since the writeToFile function doesn't write a newline character after each value;
Moreover that local results = {} is useless and in both functions the file handle hfile is not closed. This latter is very bad practice: it could waste system resources and if your script fails part of the supposedly written data could never make its way to disk, since it may be still stuck in some buffer. File handles are automatically closed when the script ends, but only if it ends in a sane way.
Unless you did some error in pasting the code or omitted significant parts of it or the book is building some example incrementally, I dare say it is fairly crappy.
If you want a quick and dirty way to save and retrieve some globals you could use this:
function writeToFile( filename, resTable )
local hfile = io.open(filename, "w")
if hfile == nil then return end
for _, name in ipairs( resTable ) do
local value = _G[name]
if value ~= nil then
hfile:write( name, " = ")
local vtype = type( value )
if vtype == 'string' then
hfile:write( string.format( "%q", value ) )
elseif vtype == 'number' or vtype == 'boolean' then
hfile:write( tostring( value ) )
else
-- do nothing - unsupported type
end
hfile:write( "\n" )
end
end
hfile:close()
end
readFromFile = dofile
It saves the globals as a Lua script and reads them back by executing the script using Lua dofile function. Its main limitation is that it can only save strings, booleans an numbers, but usually this is enough while learning.
You can test it with the following statements:
a = 10
b = "20"
c = "hello"
d = true
print( a, b, c, d )
writeToFile( "datafile", { "a", "b", "c", "d" } )
a, b, c, d = nil
print( a, b, c, d )
readFromFile( "datafile" )
print( a, b, c, d )
If you need more advanced serialization techniques you can refer to Lua WIKI page on table serialization.
Those aren't generalized "read/write any table from/to any file" functions. They apparently expect the name of a global table as an argument, not a [reference to a local] table itself. They look like the kind of one-off solution to a very specific problem that tends to show up in books. :-)
Your functions shouldn't be doing anything with _G. I don't have an API reference handy, but the read loop should be doing something like
resTable[a] = line
and the write loop would be doing
hfile:write(resTable[i])
Throw out that local "results" table too. :-)
This code reads and writes data from a file into global variables whose names are specified in aryTable. Since your file is empty, readFromFile does not actually set the variable values. And then writeToFile fails when trying to get the variable values, because they haven't been set.
Try putting data in the file so that the variables do get set, or set the variable values yourself before writing them to the file (e.g. Score = 10, etc.)

In Lua, is there a function that given a function, it returns its name as a string?

Sorry if this is too obvious, but I am a total newcomer to lua, and I can't find it in the reference.
Is there a NAME_OF_FUNCTION function in Lua, that given a function gives me its name so that I can index a table with it? Reason I want this is that I want to do something like this:
local M = {}
local function export(...)
for x in ...
M[NAME_OF_FUNCTION(x)] = x
end
end
local function fun1(...)
...
end
local function fun2(...)
...
end
.
.
.
export(fun1, fun2, ...)
return M
There simply is no such function. I guess there is no such function, as functions are first class citizens. So a function is just a value like any other, referenced to by variable. Hence the NAME_OF_FUNCTION function wouldn't be very useful, as the same function can have many variable pointing to it, or none.
You could emulate one for global functions, or functions in a table by looping through the table (arbitrary or _G), checking if the value equals x. If so you have found the function name.
a=function() print"fun a" end
b=function() print"fun b" end
t={
a=a,
c=b
}
function NameOfFunctionIn(fun,t) --returns the name of a function pointed to by fun in table t
for k,v in pairs(t) do
if v==fun then return k end
end
end
print(NameOfFunctionIn(a,t)) -- prints a, in t
print(NameOfFunctionIn(b,t)) -- prints c
print(NameOfFunctionIn(b,_G)) -- prints b, because b in the global table is b. Kind of a NOOP here really.
Another approach would be to wrap functions in a table, and have a metatable set up that calls the function, like this:
fun1={
fun=function(self,...)
print("Hello from "..self.name)
print("Arguments received:")
for k,v in pairs{...} do print(k,v) end
end,
name="fun1"
}
fun_mt={
__call=function(t,...)
t.fun(t,...)
end,
__tostring=function(t)
return t.name
end
}
setmetatable(fun1,fun_mt)
fun1('foo')
print(fun1) -- or print(tostring(fun1))
This will be a bit slower than using bare functions because of the metatable lookup. And it will not prevent anyone from changing the name of the function in the state, changing the name of the function in the table containing it, changing the function, etc etc, so it's not tamper proof. You could also strip the tables of just by indexing like fun1.fun which might be good if you export it as a module, but you loose the naming and other tricks you could put into the metatable.
Technically this is possible, here's an implementation of the export() function:
function export(...)
local env = getfenv(2);
local funcs = {...};
for i=1, select("#", ...) do
local func = funcs[i];
for local_index = 1, math.huge do
local local_name, local_value = debug.getlocal(2, local_index);
if not local_name then
break;
end
if local_value == func then
env[local_name] = local_value;
break;
end
end
end
return env;
end
It uses the debug API, would require some changes for Lua 5.2, and finally I don't necessarily endorse it as a good way to write modules, I'm just answering the question quite literally.
Try this:
http://pgl.yoyo.org/luai/i/tostring
tostring( x ) should hopefully be what you are looking for
If I am not wrong (and I probably will, because I actually never programmed in Lua, just read a bunch of papers and articles), internally there is already a table with function names (like locals and globals in Python), so you should be able to perform a reverse-lookup to see what key matches a function reference.
Anyway, just speculating.
But the fact is that looking at your code, you already know the name of the functions, so you are free to construct the table. If you want to be less error prone, it would be easier to use the name of the function to get the function reference (with eval or something like that) than the other way around.

Meta metatables?

I'm trying to make an __index function in my table which can process ALL of the field it receives.. What I want to do is that if I call the table in the following way
mytable.str1.str2.str3
I should be able to return the table
{"str1", "str2", "str3"}
Note that str1,str2,str3 are undefined, they are just strings. I am not trying to create subtables str1, str2, I just want __index to see everything beyond the first period.
Unfortunately what I have seems that __index only captures str1, and complains that "attempt to index field 'str1' (a nil value)"
Anyone know how this can be done?
I'm not sure why you'd want to do this, but here's how you do it. The comments explain the trick, but basically you need a second metatable to handle the table that's returned from the first call to the __index metamethod.
If this isn't clear, let me know and I can explain in more detail.
-- This metatable appends the new key to itself, then returns itself
stringtablemeta = {}
function stringtablemeta.__index(self, key)
table.insert(self, key)
return self
end
-- In response to the question in the comments:
function stringtablemeta.__tostring(self)
local str = ""
for i, v in ipairs(self) do
if i > 1 then str = str .. "-" end
str = str .. v
end
return str
end
-- This metatable creates a new table, with stringmetatable as its metatable
mytablemeta = {}
function mytablemeta.__index(self, key)
local temp = { key }
setmetatable(temp, stringtablemeta)
return temp
end
-- set mytable to have mymetatable as it's metatable. This makes it so when
-- you index into it, it will call the mytablemeta.__index method.
--
-- That will return a talb with a single string, the key that was passed
-- in. that table will have it's own metatable, the stringmetatable, which
-- will cause it to append keys that are called on it with its own __index
-- metamethod
mytable = {}
setmetatable(mytable, mytablemeta)
test = mytable.str1.str2.str3
for k, v in pairs(test) do
print(k, v)
end
It can't. Not without having a metatable on each of those tables.
mytable is a table. str1 is a different table. So you can do the same thing by doing this:
local temp = mytable.str1
temp.str2.str3
And as far as Lua is concerned, these are equivalent. Therefore, the only way to know what was done at each stage is to give all of them a special metatable. How you concatenate the different values into a table is something you'll have to investigate on your own.
As Nicol said, you cannot do that directly in Lua. However, by returning specially crafted tables, you can achieve a similar result to what you want. Take a look at AutomagicTables at the Lua-users Wiki for inspiration.

Use a table's content as a key

Is there an easy way to create a dictionary-like collection, i.e.
Tables can be used as keys
Tables with the same content are considered equivalent (instead of the default pointer comparison)
e.g. after
t = createCustomTable()
k1 = {'a','b','c'}
k2 = {'a','b','c'}
t[k1] = true
t[k2] should evaluate to true.
Also t itself should be usable as a key in the same way.
Is there any way to do this without
Re-implementing hash tables
Converting k1 and k2 to strings? (this is what I am currently doing.)
Serializing the two tables into strings is the solution Roberto Ierusalimschy (chief architect of Lua) recommends for indexing by content in Programming in Lua 2nd Edition.
If all of your key tables are arrays of strings (with no embedded nulls), this can be done quickly with table.concat(t,'\0'). (Obviously, your table will need to be sorted if you want index-independent identity.)
If the tables to be used as keys are fixed and their contents do not change you could build a SHA2 digest on demand in a newindex metamethod for t and use the digest as the real key. The digest would be cached in another table indexed by the real tables.
You can implement and set the __eq method in the metatable of the two tables.
k1 = {'a','b','c'}
k2 = {'a','b','c'}
mt1={__eq=function(a,b)
for k,v in pairs(a) do
if b[k]~=v then return false end
end
for k,v in pairs(b) do
if a[k]~=v then return false end
end
return true
end
}
setmetatable(k1,mt1)
setmetatable(k2,mt1)
assert(k1==k2,"Table comparison failed")
function newDict(orig)
if orig then
return orig
else
local mt2={}
local lookup ={} -- lookup table as upvalue to the metamethods
mt2.__newindex = function(t,k,v) -- Registering a new table
if type(k)~="table" then return end
if v then -- v ~= false
local found
for idx,val in pairs(lookup) do
if k==val then
found=1
break
end -- If already seen, skip.
end
if not found then
lookup[#lookup+1]=k -- not seen before, add
end
else -- v == false or nil
local to_erase
for idx,val in pairs(lookup) do -- Assume there is only one match in the dict.
if k==val then
lookup[k]=nil
break
end --don't continue after this, next will be confused.
end
end
end
mt2.__index = function(t,k) -- looking up a table
for idx,val in pairs(lookup) do
if k==val then
return true
end
end
return false
end
return setmetatable({},mt2)
end
end
t1 = newDict()
t2 = newDict()
k1={1,2,3}
k2={"a"}
k3={"z","d","f"}
k1b={1,2,3}
k2b={"a"}
k3b={"z","d","f"}
setmetatable(k1,mt1)
setmetatable(k2,mt1)
setmetatable(k3,mt1)
setmetatable(k1b,mt1)
setmetatable(k2b,mt1)
setmetatable(k3b,mt1)
-- Test multiple entries in 1 dict
t1[k1]=true
t1[k2]=true
assert(t1[k1b],"t1[k1b] did not return true")
assert(t1[k2b],"t1[k2b] did not return true")
-- Test confusion between 2 dicts
t2[k3]=true
assert(not t1[k3b],"t1[k3b] did return true")
assert(not t2[k1b],"t2[k1b] did return true")
The comparison can be implemented faster because now common entries are checked twice, but you get the point.
I can't comment on performance as it does use metatable lookups rather heavily, and needs to go through all tables on each comparison or assignment, but since you don't want to hash the tables or convert them to strings (aka serialize them) it's the only way. If I were you I'd seriously consider checking against a serialization of the tables instead of the above approach though.
This("Keys are references" section) says that keys are references to objects so using an identical table like in your example won't work. I think the way you are currently doing it may be the best way, but i could be wrong.
If you can stand a library dependency you could use something like Penlight which seems to offer sets http://penlight.luaforge.net/#T10.

Resources