function f(...)
return ...
end
And I call it like this:
f()
Example
a = f()
print(a) -- echoes 'nil', same as print(nil)
But
print(f()) -- echoes newline, same as print(), that is, no args
t = {f()} -- same as t = {}
So, what does f() return?
Update: did not know that functions can return 'void', found this http://lua-users.org/lists/lua-l/2011-09/msg00289.html meanwhile.
It returns all parameters you called it with.
f() -- has no parameter, returns nothing
If you do an assignment with less values than you have variables, i.e.
local a, b = 3
local c
Then that'll just end up with b and c being nil.
On the other hand, this would all do something:
f(1) -- returns 1
f(1, 2, 3) -- returns 1, 2 and 3
local t = {f(1, 2, 3)} -- table with the values 1, 2 and 3
The answer regarding the type could be the output of this command:
print(type(f()))
In this case, it prints:
bad argument #1 to 'type' (value expected)
So, a value is expected, but there is no value. => It returns nothing (void).
So, it's a normal behaviour to have: t = {f()} <=> t = {}
Regarding the assignment, Lua assigns by default the nil value if there is no value.
I found that Lua function can return 'nothing', not even a nil. In this case, f() returns nothing. Using nothing (without assignment) results in zero arguments in another function call(like print(f()) or in table constructor({f()}).
print(a) echoed nil because a had no assigned value, print(any_name) will echo nil aswell.
Related
This question has some reference to the question Evaluating expression in Lua in Mathematics Environment
The following code works.
tbl = {}
tbl.sin = math.sin
tbl.cos = math.cos
function mathEval(exp)
return load("return " .. exp, exp, "t", tbl)()
end
print(mathEval("sin(0)"))
print(mathEval("sin(0)+cos(1)+2^2"))
However, the following code does not work.
tbl = {}
tbl.sin = math.sin
tbl.cos = math.cos
function mathEval(exp)
return load("return " .. tostring(exp), tostring(exp), "t", tbl)()
end
print(mathEval(sin(0)))
print(mathEval(sin(0)+cos(1)+2^2))
I want to evaluate expressions without using quotes. How can that be done?
The problem with the line print(mathEval(sin(0)+cos(1)+2^2)) is that the argument of mathEval is evaluated before mathEval runs, so evaluating the variables sin and cos can not be deferred to the environment of mathEval; that is, mathEval gets a value, and no expression to evaluate at all!
First of all, one option to evaluate such mathematical expressions without the use of mathEval would be to simply temporarily change your environment:
local prev_env = _ENV -- this is needed to restore the environment later on
_ENV = tbl -- enter custom environment
local result = sin(0)+cos(1)+2^2
_ENV = prev_env -- restore environment
print(result)
if you want mathEval as a convenience helper, you'll have to pass the expression as a function returning the value to the expression such that calling the function will evaluate the expression; this allows you to defer the initialization. You'll have to use a powerful function called setfenv which allows you to change the environment of func; this was unfortunately removed in favor of _ENV in Lua 5.2 and later. The code then becomes trivial:
local function mathEval(func)
setfenv(func, tbl)
return func
end
mathEval(function() return sin(0)+cos(1)+2^2 end)
setfenv can be replicated in Lua 5.2 using the debug library, since Lua internally implements _ENV as an upvalue, as shown by Leafo:
local function setfenv(fn, env)
local i = 1
while true do
local name = debug.getupvalue(fn, i)
if name == "_ENV" then
debug.upvaluejoin(fn, i, (function()
return env
end), 1)
break
elseif not name then
break
end
i = i + 1
end
return fn
end
I assume you do not want to evaluate the expression before passing the result as an argument? Then you could wrap your expression into a function, which is then lazily called. It replaces the environment with tbl, executes the function, and reverts the environment.
tbl = {}
tbl.sin = math.sin
tbl.cos = math.cos
function mathEval(func)
local old = _ENV
_ENV = tbl
local r = func()
_ENV = old
return r
end
print(mathEval(function() return sin(0)+cos(1)+2^2 end))
You are passing sin(0) and sin(0)+cos(1)+2^2 to the mathEval(exp) function, but since sin and cos are not global variables and the math library is not being passed as an environment, Lua is unable to find them.
If you want to use math functions with the mathEval function, you can use the tbl table created before, to call the math functions, for example:
print(mathEval("tbl.sin(0)"))
print(mathEval("tbl.sin(0)+tbl.cos(1)+2^2"))
or alternatively you can pass the math library or the math functions as an upvalue to the load() function:
function mathEval(exp)
return load("return " .. tostring(exp), tostring(exp), "t", _ENV)()
end
print(mathEval("math.sin(0)"))
print(mathEval("math.sin(0)+math.cos(1)+2^2"))
It is possible to do this without quotes, this should work:
expression = string.format("%s(%d)", "math.sin", 0)
fn = load( "return " .. expression)
result = fn()
print(result) -- Output: 0
In this example, the string.format() function is used to create the string "math.sin(0)". The resulting string is then passed to the load() function as before.
In this way you don't need to use quotes on the string, and you can use placeholders to concatenate the variables.
I have searched on line and can not find an explanation for this.
What does the following mean?
Local a,b = foo()
Why two variables?
Does it mean
Local a= foo()
And
Local b= foo()
As well? Or?
Can someone please provide examples of usage?
In Lua, a function can return more than one value. For example, this function returns two values:
function returnThreeAndFive()
return 3, 5
end
You can load the results of function calls into variables using the syntax you described. (However, if you want the variables to be local, you should use a lower-case "l" for "local", not an upper-case "L".)
local a, b = returnThreeAndFive()
print(a, b) -- Prints "3 5"
If you use more variables than the function returns values, then Lua fills those in with nil.
local a, b, c = returnThreeAndFive()
print(a, b, c) -- Prints "3 5 nil"
Conversely, if the function returns more values than you use variables for, then you only capture the values that you put in variables.
local a = returnThreeAndFive()
print(a) -- Prints "3"
It means that foo returns two values and they go into a and b! So, foo is called once, but the two results get stored into two variables.
See https://www.lua.org/pil/5.1.html for further reference.
The syntax is a bit strange, other languages would ask to use braces, like
( a, b ) = foo()
Kotlin allows for that, too. They call it destructing.
Continuing to learn Lua.
I have wrote a function that removes the first sentence from each line and returns the result as a table of modified lines, where the first sentence was removed. Strangely, table.insert behaves weird in such function.
function mypackage.remove_first(table_of_lines)
local lns = table_of_lines
local new_lns = {}
for i=1,#lns do
table.insert(new_lns,string.gsub(lns[i],"^[^.]+. ","",1))
end
return new_lns
end
Unexpectedly, this gave me the following error.
[string "function mypackage.remove_first(table_of_lines)..."]:5: bad argument #2 to 'insert' (number expected, got string)
Why is "number expected" in the first place?
From table.insert docs
Inserts element value at position pos in list, shifting up the
elements list[pos], list[pos+1], ยทยทยท, list[#list]. The default value
for pos is #list+1, so that a call table.insert(t,x) inserts x at the
end of list t.
Nothing is said about type requirements for table.insert. Ok, I decided to modify the example.
function mypackage.remove_first(table_of_lines)
local lns = table_of_lines
local new_lns = {}
for i=1,#lns do
local nofirst = string.gsub(lns[i],"^[^.]+. ","",1)
table.insert(new_lns,nofirst)
end
return new_lns
end
And now everything works. Can you explain what is going on here?
The problem is a bit complicated. It's a collision of three factors:
string.gsub returns two parameters; the second parameter is the number of matches.
table.insert can take 3 parameters. When it is given 3 parameters, the second parameter is expected to be an integer offset defining where to insert the object.
When you do this: func1(func2()), all of the return values of func2 are passed to func1, so long as you don't pass arguments after func2 in func1's argument list. So func1(func2(), something_else) will get only 2 arguments.
Therefore, when you do table.insert(ins, string.gsub(...)), this will invoke the 3-argument version, which expects the second argument to be the index to insert the object into. Hence the problem.
If you want to ensure discarding, then you can wrap the expression in parenthesis:
table.insert(new_lns, (string.gsub(lns[i], "^[^.]+. ", "", 1)))
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.
I'm trying to serialize and deserialize a Lua closure
my basic understanding is that the below factory should generate closures (and that Lua doesn't much distinguish between functions and closures -- i.e. there is no type 'closure')
> function ffactory(x) return function() return x end end
> f1 = ffactory(5)
> print(f1())
5 <-- so far so good
> s = string.dump(f1)
> f2 = load(s)
> print(f2())
table: 00000000002F7BA0 <-- expected the integer 5
> print(f2()==_ENV)
true <-- definitely didn't expect this!
I expected the integer 5 to be serialized with f1. Or, if string.dump can't handle closures, i expected an error.
I get quite different (but more what I expected) results with a mild change. It looks like f2 is indeed a closure, but string.dump didn't attempt to serialize the value of x at the time it was serialized.
The docs don't help me much. (what do they mean by "...with new upvalues"?)
> function ffactory(x) return function() return x+1 end end
> f1 = ffactory(5)
> print(f1())
6 <-- good
> s = string.dump(f1)
> f2 = load(s)
> print(f2())
stdin:1: attempt to perform arithmetic on upvalue 'x' (a table value)
stack traceback:
stdin:1: in function 'f2'
stdin:1: in main chunk
[C]: in ?
You can do something like this to save/restore those upvalues (note it doesn't handle upvalues shared between different functions):
local function capture(func)
local vars = {}
local i = 1
while true do
local name, value = debug.getupvalue(func, i)
if not name then break end
vars[i] = value
i = i + 1
end
return vars
end
local function restore(func, vars)
for i, value in ipairs(vars) do
debug.setupvalue(func, i, value)
end
end
function ffactory(x) return function() return x end end
local f1 = ffactory(5)
local f2 = (loadstring or load)(string.dump(f1))
restore(f2, capture(f1)) --<-- this restored upvalues from f1 for f2
print(f1(), f2())
This works under both Lua 5.1 and Lua 5.2.
Note an interesting result if you change ffactory slightly (added math.abs(0); anything that uses global table in any way will do):
function ffactory(x) return function() math.abs(0) return x end end
Now if you restore upvalues you get the same result, but if you don't restore upvalues you get a run-time error under Lua 5.2:
lua.exe: upvalues.lua:19: attempt to index upvalue '_ENV' (a nil value)
stack traceback:
upvalues.lua:19: in function 'f2'
upvalues.lua:24: in main chunk
[C]: in ?
the docs are pretty clear. string.dump doesn't handle closures using upvalues. this is because the upvalues could be anything (including userdata, and how would Lua know how to serialize that?)
upvalues are the external variables that are local to a function due to scoping/closures. since x in your example is an upvalue to the function returned by ffactory, it does not get serialized.
if you want to support this somehow, you would have to store the upvalues yourself and set them again after you've deserialized the function, like this:
function ffactory(x)
return function() return x+1 end
end
local f1 = ffactory(5)
print(f1())
local s = string.dump(f1)
f2 = loadstring(s)
debug.setupvalue(f2, 1, 5)
print(f2())