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

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.

Related

Is there a way to duplicate a table in lua that is alterable without it affecting the original? [duplicate]

Recently I wrote a bit of Lua code something like:
local a = {}
for i = 1, n do
local copy = a
-- alter the values in the copy
end
Obviously, that wasn't what I wanted to do since variables hold references to an anonymous table not the values of the table themselves in Lua. This is clearly laid out in Programming in Lua, but I'd forgotten about it.
So the question is what should I write instead of copy = a to get a copy of the values in a?
Table copy has many potential definitions. It depends on whether you want simple or deep copy, whether you want to copy, share or ignore metatables, etc. There is no single implementation that could satisfy everybody.
One approach is to simply create a new table and duplicate all key/value pairs:
function table.shallow_copy(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
copy = table.shallow_copy(a)
Note that you should use pairs instead of ipairs, since ipairs only iterate over a subset of the table keys (ie. consecutive positive integer keys starting at one in increasing order).
Just to illustrate the point, my personal table.copy also pays attention to metatables:
function table.copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
There is no copy function sufficiently widely agreed upon to be called "standard".
To play a little readable-code-golf, here's a short version that handles the standard tricky cases:
tables as keys,
preserving metatables, and
recursive tables.
We can do this in 7 lines:
function copy(obj, seen)
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end
return res
end
There is a short write-up of Lua deep-copy operations in this gist.
Another useful reference is this Lua-users wiki page, which includes an example on how to avoid the __pairs metamethod.
The full version of deep copy, handling all the 3 situations:
Table circular reference
Keys which are also tables
Metatable
The general version:
local function deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no
if type(o) == 'table' then
no = {}
seen[o] = no
for k, v in next, o, nil do
no[deepcopy(k, seen)] = deepcopy(v, seen)
end
setmetatable(no, deepcopy(getmetatable(o), seen))
else -- number, string, boolean, etc
no = o
end
return no
end
Or the table version:
function table.deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no = {}
seen[o] = no
setmetatable(no, deepcopy(getmetatable(o), seen))
for k, v in next, o, nil do
k = (type(k) == 'table') and k:deepcopy(seen) or k
v = (type(v) == 'table') and v:deepcopy(seen) or v
no[k] = v
end
return no
end
Based on the lua-users.org/wiki/CopyTable's and Alan Yates' functions.
An optionally deep, graph-general, recursive version:
function table.copy(t, deep, seen)
seen = seen or {}
if t == nil then return nil end
if seen[t] then return seen[t] end
local nt = {}
for k, v in pairs(t) do
if deep and type(v) == 'table' then
nt[k] = table.copy(v, deep, seen)
else
nt[k] = v
end
end
setmetatable(nt, table.copy(getmetatable(t), deep, seen))
seen[t] = nt
return nt
end
Perhaps metatable copy should be optional also?
Here's what I actually did:
for j,x in ipairs(a) do copy[j] = x end
As Doub mentions, if your table keys are not strictly monotonically increasing, it should be pairs not ipairs.
I also found a deepcopy function that is more robust:
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
It handles tables and metatables by calling itself recursively (which is its own reward). One of the clever bits is that you can pass it any value (whether a table or not) and it will be copied correctly. However, the cost is that it could potentially overflow the stack. So and even more robust (non-recursive) function might be needed.
But that's overkill for the very simple case of wanting to copy an array into another variable.
The (unfortunately lightly documented) stdlib project has a number of valuable extensions to several of the libraries shipped with the standard Lua distribution. Among them are several variations on the theme of table copying and merging.
This library is also included in the Lua for Windows distribution, and should probably be a part of any serious Lua user's toolbox.
One thing to make sure of when implementing things like this by hand is the proper handling of metatables. For simple table-as-structure applications you probably don't have any metatables, and a simple loop using pairs() is an acceptable answer. But if the table is used as a tree, or contains circular references, or has metatables, then things get more complex.
Don't forget that functions are also references, so if you wanted to completely 'copy' all of the values you'd need to get separate functions, too; however, the only way I know to copy a function is to use loadstring(string.dump(func)), which according to the Lua reference manual, doesn't work for functions with upvalues.
do
local function table_copy (tbl)
local new_tbl = {}
for key,value in pairs(tbl) do
local value_type = type(value)
local new_value
if value_type == "function" then
new_value = loadstring(string.dump(value))
-- Problems may occur if the function has upvalues.
elseif value_type == "table" then
new_value = table_copy(value)
else
new_value = value
end
new_tbl[key] = new_value
end
return new_tbl
end
table.copy = table_copy
end
I think the reason why Lua doesn't have 'table.copy()' in its standard libraries is because the task is not precise to define. As shown already here, one can either make a copy "one level deep" (which you did), a deepcopy with or without caring of possible duplicate references. And then there's metatables.
Personally, I would still like them to offer a built-in function. Only if people wouldn't be pleased with its semantics, they would need to go do it themselves. Not very often, though, one actually has the copy-by-value need.
Warning: the marked solution is INCORRECT!
When the table contains tables, references to those tables will still be used instead. I have been searching two hours for a mistake that I was making, while it was because of using the above code.
So you need to check if the value is a table or not. If it is, you should call table.copy recursively!
This is the correct table.copy function:
function table.copy(t)
local t2 = {};
for k,v in pairs(t) do
if type(v) == "table" then
t2[k] = table.copy(v);
else
t2[k] = v;
end
end
return t2;
end
Note: This might also be incomplete when the table contains functions or other special types, but that is possible something most of us don't need. The above code is easily adaptable for those who need it.
That's as good as you'll get for basic tables. Use something like deepcopy if you need to copy tables with metatables.
In most of the cases when I needed to copy a table, I wanted to have a copy that doesn't share anything with the original, such that any modification of the original table has no impact on the copy (and vice versa).
All the snippets that have been shown so far fail at creating a copy for a table that may have shared keys or keys with tables as those are going to be left pointing to the original table. It's easy to see if you try to copy a table created as: a = {}; a[a] = a. deepcopy function referenced by Jon takes care of that, so if you need to create a real/full copy, deepcopy should be used.
Use penlight library here:
https://stevedonovan.github.io/Penlight/api/libraries/pl.tablex.html#deepcopy
local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)
Just use the
local unpack = unpack or table.unpack
list2 = {unpack (list)}
This might be the simplest method:
local data = {DIN1 = "Input(z)", DIN2 = "Input(y)", AINA1 = "Input(x)"}
function table.copy(mytable) --mytable = the table you need to copy
newtable = {}
for k,v in pairs(mytable) do
newtable[k] = v
end
return newtable
end
new_table = table.copy(data) --copys the table "data"
In my situation, when the information in the table is only data and other tables (excluding functions, ...), is the following line of code the winning solution:
local copyOfTable = json.decode( json.encode( sourceTable ) )
I'm writing Lua code for some home automation on a Fibaro Home Center 2. The implementation of Lua is very limited with no central library of functions you can refer to. Every function needs to be declared in the code so to keep the code serviceable, so one line solutions like this are favorable.

Use of _ENV in Lua function does not have effect

I'm reviewing some toy examples from Lua and I found the following one over there with respect to environments:
M = {} -- the module
complex = {} -- global complex numbers registry
mt = {} --metatable for complex numbers
function new (r, i)
local cp = {}
cp = {r=r, i=i}
return setmetatable(cp,mt)
end
M.new = new -- add 'new' to the module
function M.op (...)
--Why does not it work?
local _ENV = complex
return ...
end
function M.add (c1, c2)
return new(c1.r + c2.r, c1.i + c2.i)
end
function M.tostring (c)
return string.format("(%g,%g)", c.r, c.i) --to avoid +-
end
mt.__tostring = M.tostring
mt.__add = M.add
complex.a = M.new(4,3)
complex.b = N.new(6,2)
--nil
M.op(a+b)
--It works
M,op(complex.a+complex.b)
The use of _ENV has no effect. However, if I use complex = _G, both lines work. How do set a local environment for M.op. I'm not asking for specific libraries, I just want to know why it does not work and how to fix it.
M.op(a+b)
This line doesn't do what you expect, because it uses values of a and b that are available when this method is called. It doesn't matter that you set _ENV value inside the method, as by the time the control gets there, the values referenced by a and b have already been retrieved and since both values are nil in your code, you probably get "attempt to perform arithmetic on global..." error.
how to fix it.
I'm not sure what exactly you want to fix, as you already reference the example that works. If you assign complex.a you can't assume that a will have the same value without mapping complex table to _ENV.

Lua - get table hex identifier

I want to know how to get the table hex id. I know that doing:
local some_var = {}
print (some_var)
the result is (for instance):
table: 0x21581c0
I want the hex without the table: string. I know that maybe some of you suggest me to make a regular expression (or something similar) to remove those chars, but I want to avoid that, and just get the 0x21581c0
Thanks
This is simpler and works for all types that are associated with pointers:
local function getId(t)
return string.format("%p", t)
end
print("string:", getId("hi"))
print("table:", getId({}))
print("userdata:", getId(io.stdin))
print("function:", getId(print))
print("number:", getId(1))
print("boolean:", getId(false))
print("nil:", getId(nil))
Result:
string: 0x0109f04638
table: 0x0109f0a270
userdata: 0x01098076c8
function: 0x0109806018
number: NULL
boolean: NULL
nil: NULL
In the standard implementation, there is the global 'print' variable that refers to a standard function that calls, through the global variable 'tostring', a standard function described here. The stanard 'tostring' function is the only way to retrieve the hexadecimal number it shows for a table.
Unfortunately, there is no configuration for either of the functions to do anything differently for all tables.
Nonetheless, there are several points for modification. You can create you own function and call that every time instead, or point either of the the global variables print or tostring to you own functions. Or, set a __tostring metamethod on each table you need tostring to return a different answer for. The advantage to this is it gets you the format you want with only one setup step. The disadvantage is that you have to set up each table.
local function simplifyTableToString(t)
local answer = tostring(t):gsub("table: ", "", 1)
local mt = getmetatable(t)
if not mt then
mt = {}
setmetatable(t, mt)
end
mt.__tostring = function() return answer end
end
local a = {}
local b = {}
print(a, b)
simplifyTableToString(a)
print(a, b)
Without complex patterns, you can just search for the first space, and grab the substring of what follows.
function get_mem_addr (object)
local str = tostring(object)
return str:sub(str:find(' ') + 1)
end
print(get_mem_addr({})) -- 0x109638
print(get_mem_addr(function () end)) -- 0x108cf8
This function will work with tables and functions, but expect errors if you pass it anything else.
Or you can use a little type checking:
function get_mem_addr (o)
return tostring(o):sub(type(o):len() + 3)
end
The table id stated by the OP is invalid in the version of Lua I am using (5.1 in Roblox). A valid ID is length 8, not 9 as in your example. Either way, just use string.sub to get the sub-string you are after.
string.sub(tostring({}), 8)
The reason is, 'table: ' is 7 characters long, so we take from index 8 through the end of the string which returns the hex value.

Lua equals function and tables [duplicate]

Recently I wrote a bit of Lua code something like:
local a = {}
for i = 1, n do
local copy = a
-- alter the values in the copy
end
Obviously, that wasn't what I wanted to do since variables hold references to an anonymous table not the values of the table themselves in Lua. This is clearly laid out in Programming in Lua, but I'd forgotten about it.
So the question is what should I write instead of copy = a to get a copy of the values in a?
Table copy has many potential definitions. It depends on whether you want simple or deep copy, whether you want to copy, share or ignore metatables, etc. There is no single implementation that could satisfy everybody.
One approach is to simply create a new table and duplicate all key/value pairs:
function table.shallow_copy(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
copy = table.shallow_copy(a)
Note that you should use pairs instead of ipairs, since ipairs only iterate over a subset of the table keys (ie. consecutive positive integer keys starting at one in increasing order).
Just to illustrate the point, my personal table.copy also pays attention to metatables:
function table.copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
There is no copy function sufficiently widely agreed upon to be called "standard".
To play a little readable-code-golf, here's a short version that handles the standard tricky cases:
tables as keys,
preserving metatables, and
recursive tables.
We can do this in 7 lines:
function copy(obj, seen)
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end
return res
end
There is a short write-up of Lua deep-copy operations in this gist.
Another useful reference is this Lua-users wiki page, which includes an example on how to avoid the __pairs metamethod.
The full version of deep copy, handling all the 3 situations:
Table circular reference
Keys which are also tables
Metatable
The general version:
local function deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no
if type(o) == 'table' then
no = {}
seen[o] = no
for k, v in next, o, nil do
no[deepcopy(k, seen)] = deepcopy(v, seen)
end
setmetatable(no, deepcopy(getmetatable(o), seen))
else -- number, string, boolean, etc
no = o
end
return no
end
Or the table version:
function table.deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no = {}
seen[o] = no
setmetatable(no, deepcopy(getmetatable(o), seen))
for k, v in next, o, nil do
k = (type(k) == 'table') and k:deepcopy(seen) or k
v = (type(v) == 'table') and v:deepcopy(seen) or v
no[k] = v
end
return no
end
Based on the lua-users.org/wiki/CopyTable's and Alan Yates' functions.
An optionally deep, graph-general, recursive version:
function table.copy(t, deep, seen)
seen = seen or {}
if t == nil then return nil end
if seen[t] then return seen[t] end
local nt = {}
for k, v in pairs(t) do
if deep and type(v) == 'table' then
nt[k] = table.copy(v, deep, seen)
else
nt[k] = v
end
end
setmetatable(nt, table.copy(getmetatable(t), deep, seen))
seen[t] = nt
return nt
end
Perhaps metatable copy should be optional also?
Here's what I actually did:
for j,x in ipairs(a) do copy[j] = x end
As Doub mentions, if your table keys are not strictly monotonically increasing, it should be pairs not ipairs.
I also found a deepcopy function that is more robust:
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
It handles tables and metatables by calling itself recursively (which is its own reward). One of the clever bits is that you can pass it any value (whether a table or not) and it will be copied correctly. However, the cost is that it could potentially overflow the stack. So and even more robust (non-recursive) function might be needed.
But that's overkill for the very simple case of wanting to copy an array into another variable.
The (unfortunately lightly documented) stdlib project has a number of valuable extensions to several of the libraries shipped with the standard Lua distribution. Among them are several variations on the theme of table copying and merging.
This library is also included in the Lua for Windows distribution, and should probably be a part of any serious Lua user's toolbox.
One thing to make sure of when implementing things like this by hand is the proper handling of metatables. For simple table-as-structure applications you probably don't have any metatables, and a simple loop using pairs() is an acceptable answer. But if the table is used as a tree, or contains circular references, or has metatables, then things get more complex.
Don't forget that functions are also references, so if you wanted to completely 'copy' all of the values you'd need to get separate functions, too; however, the only way I know to copy a function is to use loadstring(string.dump(func)), which according to the Lua reference manual, doesn't work for functions with upvalues.
do
local function table_copy (tbl)
local new_tbl = {}
for key,value in pairs(tbl) do
local value_type = type(value)
local new_value
if value_type == "function" then
new_value = loadstring(string.dump(value))
-- Problems may occur if the function has upvalues.
elseif value_type == "table" then
new_value = table_copy(value)
else
new_value = value
end
new_tbl[key] = new_value
end
return new_tbl
end
table.copy = table_copy
end
I think the reason why Lua doesn't have 'table.copy()' in its standard libraries is because the task is not precise to define. As shown already here, one can either make a copy "one level deep" (which you did), a deepcopy with or without caring of possible duplicate references. And then there's metatables.
Personally, I would still like them to offer a built-in function. Only if people wouldn't be pleased with its semantics, they would need to go do it themselves. Not very often, though, one actually has the copy-by-value need.
Warning: the marked solution is INCORRECT!
When the table contains tables, references to those tables will still be used instead. I have been searching two hours for a mistake that I was making, while it was because of using the above code.
So you need to check if the value is a table or not. If it is, you should call table.copy recursively!
This is the correct table.copy function:
function table.copy(t)
local t2 = {};
for k,v in pairs(t) do
if type(v) == "table" then
t2[k] = table.copy(v);
else
t2[k] = v;
end
end
return t2;
end
Note: This might also be incomplete when the table contains functions or other special types, but that is possible something most of us don't need. The above code is easily adaptable for those who need it.
That's as good as you'll get for basic tables. Use something like deepcopy if you need to copy tables with metatables.
In most of the cases when I needed to copy a table, I wanted to have a copy that doesn't share anything with the original, such that any modification of the original table has no impact on the copy (and vice versa).
All the snippets that have been shown so far fail at creating a copy for a table that may have shared keys or keys with tables as those are going to be left pointing to the original table. It's easy to see if you try to copy a table created as: a = {}; a[a] = a. deepcopy function referenced by Jon takes care of that, so if you need to create a real/full copy, deepcopy should be used.
Use penlight library here:
https://stevedonovan.github.io/Penlight/api/libraries/pl.tablex.html#deepcopy
local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)
Just use the
local unpack = unpack or table.unpack
list2 = {unpack (list)}
This might be the simplest method:
local data = {DIN1 = "Input(z)", DIN2 = "Input(y)", AINA1 = "Input(x)"}
function table.copy(mytable) --mytable = the table you need to copy
newtable = {}
for k,v in pairs(mytable) do
newtable[k] = v
end
return newtable
end
new_table = table.copy(data) --copys the table "data"
In my situation, when the information in the table is only data and other tables (excluding functions, ...), is the following line of code the winning solution:
local copyOfTable = json.decode( json.encode( sourceTable ) )
I'm writing Lua code for some home automation on a Fibaro Home Center 2. The implementation of Lua is very limited with no central library of functions you can refer to. Every function needs to be declared in the code so to keep the code serviceable, so one line solutions like this are favorable.

What's the difference between these two Lua examples? Is one better?

I'm just getting started with Lua. In the example I'm learning from (the Ghosts & Monsters Corona open source), I see this pattern repeatedly.
local director = require("director")
local mainGroup = display.newGroup()
local function main()
mainGroup:insert(director.directorView)
openfeint = require ("openfeint")
openfeint.init( "App Key Here", "App Secret Here", "Ghosts vs. Monsters", "App ID Here" )
director:changeScene( "loadmainmenu" )
return true
end
main()
Is this some sort of convention experienced Lua programmers recommend or are there genuine advantages to doing it this way? Why wouldn't you just skip the function all together and do this:
local director = require("director")
local mainGroup = display.newGroup()
mainGroup:insert(director.directorView)
local openfeint = require ("openfeint")
openfeint.init( "App Key Here", "App Secret Here", "Ghosts vs. Monsters", "App ID Here" )
director:changeScene( "loadmainmenu" )
Is there some implicit benefit to the first style over the second? Thanks!
Is this some sort of convention experienced Lua programmers recommend or are there genuine advantages to doing it this way?
It's not typical. The advantage is that object state is private, but that's not enough of an advantage to recommend it.
I see this pattern repeatedly.
I've never seen it before, and it happens only once in the source you posted.
EDIT: Adding a response to a question asked in the comments below this post.
A function which accesses external local variables binds to those variables and is called a 'closure'. Lua (for historical reasons) refers to those bound variables as 'upvalues'. For example:
local function counter()
local i = 1
return function()
print(i)
i = i + 1
end
end
local a, b = counter(), counter()
a() a() a() b() --> 1 2 3 1
a and b are closures bound to different copies of i, as you can see from the output. In other words, you can think of a closure as function with it's own private state. You can use this to simulate objects:
function Point(x,y)
local p = {}
function p.getX() -- syntax sugar for p.getX = function()
return x
end
function p.setX(x_)
x = x_
end
-- for brevity, not implementing a setter/getter for y
return p
end
p1 = Point(10,20)
p1.setX(50)
print(p1.getX())
Point returns a table of closures, each bound to the locals x and y. The table doesn't contain the point's state, the closures themselves do, via their upvalues. An important point is that each time Point is called it creates new closures, which is not very efficient if you have large quantities of objects.
Another way of creating classes in Lua is to create functions that take a table as the first argument, with state being stored in the table:
function Point(x,y)
local p = {x=x,y=y}
function p:getX() -- syntax sugar for p.getX = function(self)
return self.x
end
function p:setX(x)
self.x = x
end
return p
end
p1 = Point(10,20)
p1:setX(50) -- syntax sugar for p1.setX(p1, 50)
print(p1:getX()) -- syntax sugar for p1.getX(p1)
So far, we're still creating new copies of each method, but now that we're not relying on upvalues for state, we can fix that:
PointClass = {}
function PointClass:getX() return self.x end
function PointClass:setX(x) self.x = x end
function Point(x,y)
return {
x = x,
y = y,
getX = PointClass.getX,
setX = PointClass.getY,
}
end
Now the methods are created once, and all Point instances share the same closures. An even better way of doing this is to use Lua's metaprogramming facility to make new Point instances automatically look in PointClass for methods not found in the instance itself:
PointClass = {}
PointClass.__index = PointClass -- metamethod
function PointClass:getX() return self.x end
function PointClass:setX(x) self.x = x end
function Point(x,y)
return setmetatable({x=x,y=y}, PointClass)
end
p1 = Point(10,20)
-- the p1 table does not itself contain a setX member, but p1 has a metatable, so
-- when an indexing operation fails, Lua will look in the metatable for an __index
-- metamethod. If that metamethod is a table, Lua will look for getX in that table,
-- resolving p1.setX to PointClass.setX.
p1:setX(50)
This is a more idiomatic way of creating classes in Lua. It's more memory efficient and more flexible (in particular, it makes it easy to implement inheritance).
I frequently write my own Lua scripts this way because it improves readability in this case:
function main()
helper1( helper2( arg[1] ) )
helper3()
end
function helper1( foo )
print( foo )
end
function helper2( bar )
return bar*bar
end
function helper3()
print( 'hello world!' )
end
main()
This way the "main" code is at the top, but I can still define the necessary global functions before it executes.
A simple trick, really. I can't think of any reason to do this besides readability.
The first style could be used too improove readability, but I would rather give the function some meaningful name instead of main or just go without the function.
By the way, I think it's always a good practice to name blocks of code, i.e. put them into functions or methods. It helps explain your intend with that piece of code, and encourages reuse.
I don't see much point to the first style as you've shown it. But if it said something like if arg then main() end at the bottom, the script might (just might) be useful as a loadable "library" in addition to being a standalone script. That said, having a main() like that smacks of C, not Lua; I think you're right to question it.

Resources