I have a Lua code below :
a, b = 1, 10
if a<b then
print(a)
local a
print(a)
end
print(a, b)
Just a small question : first of all, I created a global variable a = 1;
then in the then block I use the global variable a to print it; and then I declared a local variable a that is not initialized (thus it gets the value nil)
Then my question comes : how could I get access to the global variable a after having created the local variable a in the then block, is that possible ? If so, please give me an answer :)
Use _ENV.a to access the global variable after using the same name for a local one!
Note, Lua versions 5.1 and below use _G
Edit, Just tested this:
a, b = 1, 10
if a<b then
local a = 12
print(a) -- Will print 12
print(_ENV.a) -- Will print 1
end
print(a, b) -- Will print 1 10
And it worked fine, gave me the desired output referencing _ENV.a
Related
I was excited to learn that, as of Lua 5.4, Lua supports constant (const) and to-be-closed (close) variables! However, upon testing these keywords, they don't seem to do anything at all. I wrote the following code to sample the features to get a better grasp of their exact usage:
function f()
local const x = 3
print(x)
x = 2
print(x)
end
f()
function g()
local close x = {}
setmetatable(x, {__close = function() print("closed!") end})
end
g()
I titled the file constCheck.lua and ran it with lua constCheck.lua. The output is as follows:
3
2
I was expecting an error on my call to f(), or at least for it to print 3 twice, instead it seemed to reassign x with no issue at all. Further, I was expecting the call to g() to print out "closed!" when x left scope at the end of the function, but this did not happen. I can't find very many examples of these keywords' usage. Am I using them properly? Do they work?
Note: lua -v => Lua 5.4.0 Copyright (C) 1994-2020 Lua.org, PUC-Rio
This is <const> not const, and <close> not close
See https://lwn.net/Articles/826134/
do
local x <const> = 42
x = x+1
end
-- ERROR: attempt to assign to const variable 'x'
And some example https://github.com/lua/lua/blob/master/testes/code.lua#L11
local k0aux <const> = 0
https://github.com/lua/lua/blob/master/testes/files.lua#L128
local f <close> = assert(io.open(file, "w"))
From the Lua 5.4 Reference Manual : 3.3.7 - Local Declarations
Each variable name may be postfixed by an attribute ( a name between angle brackets):
attrib ::= [‘<’ Name ‘>’]
There are two possible attributes: const, which declares a constant
variable, that is, a variable that cannot be assigned to after its
initialization; and close, which declares a to-be-closed variable
So you would have to write local x <const> = 3 for example.
Your code local const x = 3 is equivalent to
local const = nil
x = 3
So you're actually creating a local nil value const and a global number value x.
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.
This question already has answers here:
Access local variable by name
(2 answers)
Closed 6 years ago.
Just like how we can do this:
a = 3
print(_G['a']) -- 3
I want to be able to do something like this:
local a = 3
print(_L['a']) -- 3
I basically want to be able to access local variables using their names as strings. Is there a table that can do this, perhaps one that can be passed as a function argument? It would be like the this keyword in ActionScript.
This is possible by way of the debug library - namely the getlocal and setlocal functions. If you can't use this library (or access the C API), then you're out of luck.
You can extend your global environment with a specially crafted _L table, that when accessed performs linear lookups of the current set of locals.
Reading a local variable simply finds a matching variable name, and returns its value. Writing to a local variable requires you to discover its index in the stack frame, and then update the value accordingly. Note that you cannot create new locals.
Here's a simple example that works with Lua 5.1 (but not Lua 5.2+).
local function find_local (key)
local n = 1
local name, sname, sn, value
repeat
name, value = debug.getlocal(3, n)
if name == key then
sname = name
sn = n
end
n = n + 1
until not name
return sname, sn
end
_G._L = setmetatable({}, {
metatable = false,
__newindex = function (self, key, value)
local _, index = find_local(key)
if not index then
error(('local %q does not exist.'):format(key))
end
debug.setlocal(2, index, value)
end,
__index = function (_, key)
return find_local(key)
end
})
In use:
local foo = 'bar'
print(_L['foo']) --> 'bar'
_L['foo'] = 'qux'
print(_L['foo']) --> 'qux'
local function alter_inside (key)
local a, b, c = 5, 6, 7
_L[key] = 11
print(a, b, c)
end
alter_inside('a') --> 11 6 7
alter_inside('b') --> 5 11 7
alter_inside('c') --> 5 6 11
You could write this in a different manner, using plain functions instead of the table combined with read / write operations (__index, __newindex).
See §2.4 – Metatables and Metamethods if the above use of metatables is a brand new topic for you.
In Lua 5.2+, you can use the special _ENV tables to adjust your current chunk's environment, but note that this is not the same as using local variables.
local function clone (t)
local o = {}
for k, v in pairs(t) do o[k] = v end
return o
end
local function alter_inside (key)
local _ENV = clone(_ENV)
a = 5
b = 6
c = 7
_ENV[key] = 11
print(a, b, c)
end
alter_inside('a') --> 11 6 7
alter_inside('b') --> 5 11 7
alter_inside('c') --> 5 6 11
As a final note, also consider that this (ab)use of locals might not be the best approach.
You could simply store your variables in a table, when appropriate, to achieve the same results with far less overhead. This approach is highly recommended.
local function alter_inside (key)
-- `ls` is an arbitrary name, always use smart variable names.
local ls = { a = 5, b = 6, c = 7 }
ls[key] = 11
print(ls.a, ls.b, ls.c)
end
alter_inside('a') --> 11 6 7
alter_inside('b') --> 5 11 7
alter_inside('c') --> 5 6 11
Don't dig yourself into a hole trying to solve unnecessary problems.
Suppose I have the following two Lua files:
In a.lua:
local x = 5
f = dofile'b.lua'
f()
In b.lua:
local fun = function()
print(x)
end
return fun
Then if I run luajit a.lua in shell it prints nil since x cannot be seen in the function defined in b.lua. The expected printing should be 5. However if I put everything in a single file then it's exactly what I want:
In aa.lua:
local x = 5
local f = function()
print(x)
end
f()
Run luajit aa.lua it prints 5.
So why x cannot be seen in the first case?
As their name suggests, local variables are local to the chunk.
dofile() loads the chunk from another file. Since it's another chunk, it makes sense that the local variable x in the first chunk isn't seen by it.
I agree that it is somewhat unintuitive that this doesn't work.
You'd like to say, at any point in the code there is a clear set of variables that are 'visible' -- some may be local, some may be global, but there is some map that the interpreter can use to resolve names of either kind.
When you load a chunk using dofile, then it can see whatever global variables currently exist, but apparently it can't see any local variables. We know that 'dofile' is not like C/C++ inclusion macros, which would give exactly the behavior you describe for local variables, but still you might reasonably expect that this part of it would work the same.
Ultimately there's no answer but "that's just not how they specified the language". The only satisfying answer is probably along the lines 'because otherwise it would cause non-obvious problem X' or 'because then use-case Y would go slower'.
I think the best answer is that, if all names were dynamically rebound according to the scope in which they are loaded when you use loadfile / dofile, that would inhibit a lot of optimization and such when compiling chunks into bytecode. In the lua system, name resolution works like 'either it is local in this scope, and then it binds to that (known) object, or, it is a lookup in the (unique) global table.' This system is pretty simple, there are only a few options and not a lot of room for complexity.
I don't think that running byte code even keeps track of the names of local variables, it discards them after the chunk is compiled. They would have to undo that optimization if they wanted to allow dynamic name resolution at chunk loading time like you suggest.
If your question is not really why but how can I make it work anyways, then one way you can do it is, in the host script, put any local variables that you want to be visible in the environment of the script that is called. When you do this you need to split dofile into a few calls. It's slightly different in lua 5.1 vs lua 5.2.
In lua 5.1:
In a.lua:
local shared = { x = 5 }
temp = loadfile('b.lua')
setfenv(temp, shared)
f = temp()
f()
In lua 5.2:
In a.lua:
local shared = { x = 5 }
temp = loadfile('b.lua', 't', shared)
f = temp()
f()
The x variable defined in module a.lua cannot be seen from b.lua because it was declared as local. The scope of a local variable is its own module.
If you want x to be visible from b.lua, just need to declare it global. A variable is either local or global. To declare a variable as global, just simply do not declare it as local.
a.lua
x = 5
f = dofile'b.lua'
f()
b.lua
local fun = function()
print(x)
end
return fun
This will work.
Global variables live within the global namespace, which can be accessed at any given time via the _G table. When Lua cannot solve a variable, because it's not defined inside the module where is being used, Lua searches that variable in the global namespace. In conclusion, it's also possible to write b.lua as:
local fun = function()
print(_G["x"])
end
return fun
I am creating functions (of x) from a string in Lua. The code I am using is
function fcreate(fs)
return assert(loadstring("return function (x) return " .. fs.." end"))()
end
This works for globals, e.g.
u=fcreate("math.sin(x)")
does the right thing.
However, it does not seem to like local variables. So
local c=1
u=fcreate("math.sin(x)+c")
will not work because c is local.
Is this fixable?
"loadstring does not compile with lexical scoping", so no, it can't see locals outside the loadstring call.
Is this fixable?
That depends. Why are you using loadstring in the first place? Lua supports closures as first class values, so I can't see from your example why you'd need loadstring.
Your example:
u = fcreate("math.sin(x)+c")
Can be rewritten without the need for loadstring or your fcreate function:
u = function(x) return math.sin(x)+c end
Which of course is the same as:
function u(x) return math.sin(x) + c end
I can see a case for loadstring if you have user-configurable expressions that you wanted to compile into some other function, but your case with the local c suggests that's not the case. Are you trying to make some kinda of home-rolled lamda syntax?
Can't be done in any reasonable way. For an example of why, look at this:
function makefunction(name)
local a = 1
local b = 2
local c = 3
-- ...
return assert(loadstring("return " .. name))
end
local a = 4
local func = makefunction("a")
print(func())
If this worked, what is printed? 1 or 4? Does it capture the variable from the place where the function was loaded, even though that function doesn't exist anymore? Or does it look it up from the place where it was called?
The first would mean that the function is lexically scoped wherever it's created. Being able to access the variable after the function has exited means that the variable would need to be promoted into an upvalue dynamically, which is not something that Lua can do at the moment. As it is now, Lua can see every access to a local variable during compilation, so it knows which variables to turn into upvalues (at a performance hit) and which to keep as locals.
The second would mean that variable accesses inside a loadstring'd function would work completely different than every other access in Lua: Lua uses lexical scoping, not dynamic scoping. It'd be a huge implementation change in Lua, and an extremely inconsistent one.
So, neither is supported. You can control the environment of a dynamically loaded function, using setfenv in Lua 5.1 or the env parameter of load(...) in Lua 5.2, but neither of those let you access local variables automatically.
Something you could do if you don't need to mutate the local variables is to pass those values as arguments to the generated function. You would still need to manually specify the variables to close over but its better then nothing.
For example, you can build up your closure to look like
return (function(a,b,c)
return function(x) return print(a, x) end
end)(...)
We might do that by changing your function to look like
function fcreate(variables, fs)
local varnames = {}
local varvalues = {}
local nvars = 0
for n,v in pairs(variables) do
nvars = nvars + 1
table.insert(varnames, n)
table.insert(varvalues, v)
end
local chunk_str = (
'return (function(' .. table.concat(varnames, ',') .. ') ' ..
'return function(x) return ' .. fs .. ' end ' ..
'end)(...)'
)
return assert( loadstring(chunk_str) )( unpack(varvalues, 1, nvars) )
end
local a = 1;
local f = fcreate({a=a}, 'x+a')
print(f(1), f(2))