NB: I am using Lua version 5.3.
This question is motivated by Exercise 25.1 (p. 264) of Programming in Lua (4th ed.). That exercise reads as follows:
Exercise 25.1: Adapt getvarvalue (Listing 25.1) to work with different coroutines (like the functions from the debug library).
The function getvarvalue that the exercise refers to is copied verbatim below.
-- Listing 25.1 (p. 256) of *Programming in Lua* (4th ed.)
function getvarvalue (name, level, isenv)
local value
local found = false
level = (level or 1) + 1
-- try local variables
for i = 1, math.huge do
local n, v = debug.getlocal(level, i)
if not n then break end
if n == name then
value = v
found = true
end
end
if found then return "local", value end
-- try non-local variables
local func = debug.getinfo(level, "f").func
for i = 1, math.huge do
local n, v = debug.getupvalue(func, i)
if not n then break end
if n == name then return "upvalue", v end
end
if isenv then return "noenv" end -- avoid loop
-- not found; get value from the environment
local _, env = getvarvalue("_ENV", level, true)
if env then
return "global", env[name]
else -- no _ENV available
return "noenv"
end
end
Below is my enhanced version of this function, which implements the additional functionality specified in the exercise. This version accepts an optional thread parameter, expected to be a coroutine. The only differences between this enhanced version and the original getvarvalue are:
the handling of the additional optional thread parameter;
the special setting of the level parameter depending on whether the thread parameter is the same as the running coroutine or not; and
the passing of the thread parameter in the calls to debug.getlocal and debug.getinfo, and in the recursive call.
(I have marked these differences in the source code through numbered comments.)
function getvarvalue_enhanced (thread, name, level, isenv)
-- 1
if type(thread) ~= "thread" then
-- (thread, name, level, isenv)
-- (name, level, isenv)
isenv = level
level = name
name = thread
thread = coroutine.running()
end
local value
local found = false
-- 2
level = level or 1
if thread == coroutine.running() then
level = level + 1
end
-- try local variables
for i = 1, math.huge do
local n, v = debug.getlocal(thread, level, i) -- 3
if not n then break end
if n == name then
value = v
found = true
end
end
if found then return "local", value end
-- try non-local variables
local func = debug.getinfo(thread, level, "f").func -- 3
for i = 1, math.huge do
local n, v = debug.getupvalue(func, i)
if not n then break end
if n == name then return "upvalue", v end
end
if isenv then return "noenv" end -- avoid loop
-- not found; get value from the environment
local _, env = getvarvalue_enhanced(thread, "_ENV", level, true) -- 3
if env then
return "global", env[name]
else
return "noenv"
end
end
This function works reasonably well, but I have found one strange situation1 where it fails. The function make_nasty below generates a coroutine for which getvarvalue_enhanced fails to find an _ENV variable; i.e. it returns "noenv". (The function that serves as the basis for nasty is the closure closure_B, which in turn invokes the closure closure_A. It is closure_A that then yields.)
function make_nasty ()
local function closure_A () coroutine.yield() end
local function closure_B ()
closure_A()
end
local thread = coroutine.create(closure_B)
coroutine.resume(thread)
return thread
end
nasty = make_nasty()
print(getvarvalue_enhanced(nasty, "_ENV", 2))
-- noenv
In contrast, the almost identical function make_nice produces a coroutine for which getvarvalue_enhanced succeeds in finding an _ENV variable.
function make_nice ()
local function closure_A () coroutine.yield() end
local function closure_B ()
local _ = one_very_much_non_existent_global_variable -- only difference!
closure_A()
end
local thread = coroutine.create(closure_B)
coroutine.resume(thread)
return thread
end
nice = make_nice()
print(getvarvalue_enhanced(nice, "_ENV", 2))
-- upvalue table: 0x558a2633c930
The only difference between make_nasty and make_nice is that, in the latter, the closure closure_B references a non-existent global variable (and does nothing with it).
Q: How can I modify getvarvalue_enhanced so that it is able to locate _ENV for nasty, the way it does for nice?
EDIT: changed the names of the closures within make_nasty and make_nice.
EDIT2: the wording of Exercise 25.3 (same page) may be relevant here (my emphasis):
Exercise 25.3: Write a version of getvarvalue (Listing 25.1) that returns a table with all variables that are visible at the calling function. (The returned table should not include environmental variables; instead it should inherit them from the original environment.)
This question implies that there should be a way to get at the variables that are merely visible from a function, whether the function uses them or not. Such variables would certainly include _ENV. (The author is one of Lua's creators, so he knows what he's talking about.)
1 I am sure that someone with a better understanding of what is going on in this example will be able to come up with a less convoluted way to elicit the same behavior. The example I present here is the most minimal form I can come up with of the situation I found by accident.
local function inner_closure () coroutine.yield() end
local function outer_closure ()
inner_closure()
end
The function make_nasty below generates a coroutine for which getvarvalue_enhanced fails to find an _ENV variable; i.e. it returns "noenv"
That's a correct behavior.
The closure outer_closure has upvalue inner_closure but doesn't have upvalue _ENV.
This is how lexical scope works.
It's OK that some closures don't have _ENV upvalue.
In your example the closure inner_closure isn't defined inside the body of outer_closure.
inner_closure is not nested in outer_closure.
It's impossible.
If a closure doesn't make any use of the global environment _ENV, then it doesn't have that upvalue whatsoever.
A function like
local something = 20
local function noupval(x, y)
return x * y
end
Doesn't need or have any upvalues, not even to the global environment.
This question implies that there should be a way to get at the variables that are merely visible from a function, whether the function uses them or not.
There really isn't though. You can easily confirm this by looking at the output of luac -p -l <your_code.lua>, more precisely at the upvalues of each function.
If anything, I think using the word visible is somewhat misleading there. Visibility really only matters when creating a closure, but once it has been closed, that closure only has a set of upvalues which it can access.
Exercise 25.3: Write a version of getvarvalue (Listing 25.1) that returns a table with all variables that are visible at the calling function. (The returned table should not include environmental variables; instead it should inherit them from the original environment.)
You may have misunderstood that exercise; the way I understand it is something like this:
local upvalue = 20
local function foo()
local var = upvalue -- Create 1 local and access 1 upvalue
type(print) == "function" -- Access _ENV so it becomes an upvalue
return getvarvalue_enhanced()
end
and the above would return {var = 20, upvalue = 20, _ENV = <Proxy table to _ENV>}
After all, it asks specifically about the calling function, not one you pass as a parameter.
This doesn't change the fact that you still only get _ENV if you access it though. If you don't use any globals, the function won't have any reference to _ENV whatsoever.
Related
I tried this example code to see if it actually work
--This example is from Programming in Lua Second Edition
function values (t)
local i = 0
return function ()
i = i + 1
return t[i]
end
end
t = {10, 20, 30}
iter = values(t)
while true do
local element = iter()
-- calls the iterator
if element == nil then
break
end
print(element) --> 10, 20, 30
end
I decided to change it a bit, I thought the code will get the same result but I think I was wrong
--This example is from Programming in Lua Second Edition
function values (t)
local i = 0
return function ()
i = i + 1
return t[i]
end
end
t = {10, 20, 30}
while true do
local element = values(t)()
-- calls the iterator
if element == nil then
break
end
print(element) --> This will print 10 forever if I don't stop the IDE
end
Why is iter important that much?
When you do local element = values(t)(), you are actually creating an iterator function every single time your loop is running. Take a note of your values function, see that it has a local variable called i. Every single time you are running this function, a local variable is initialized and sent to your inner function which handles the iteration. The original example is perfectly fine, however if you want to practice local and global variables, you may change the local i to a global variable i, or a different name and move it outside your function so it will not get called every time you run your iterate function (You may keep it local still, so the scope will be limited to current file). However, that is totally not recommended, since your value may be re-used some time later by another function or file, also it will limit you to one full iteration per run (Unless you set the value back to zero before retrying to iterate.)
local i = 0 -- Notice that the variable is taken out of the function.
-- With your example, the problem was that this variable was set to 0 for every
-- time you called the function.
function values (t)
return function ()
i = i + 1
return t[i]
end
end
t = {10, 20, 30}
while true do
local element = values(t)()
-- calls the iterator
if element == nil then
break
end
print(element) --> This will print 10 forever if I don't stop the IDE
end
Also keep performance in your mind, as every time you are iterating with your example, a function is being created, called, and then destroyed by the garbage collection. This is why the original example created an iterator function only once, and then called it multiple times, whereas your example creates an iterator function every time it is looping.
If all you need to do is to iterate through elements of a table, then lua already has generic for loop (pairs), check here and here. Example in your case (which also shortens your code by a lot):
local t = {10, 20, 30}
-- We don't need key in this case, but if you do, replace _ with k or
-- any other variable name you want.
for _, element in pairs(t) do
print(element)
end
Why can't the factory function fromto return the local function iter as an iterator to the for loop?
function fromto(from,to)
return iter,to,from-1
end
local function iter(to,from)--parameter:invariant state, control variable
from = from + 1
if from <= to then
return from
else
return nil
end
end
for i in fromto(1,10) do
print(i)
end
As #YuHao says, your scheme can work. There are a few ways you could rearrange your code. Here's one:
local function fromto(from,to)
--parameter:invariant state, control variable
local function iter(to,from)
from = from + 1
if from <= to then
return from
else
return nil
end
end
return iter,to,from-1
end
for i in fromto(1,10) do
print(i)
end
Two things to understand: variable scope and functions are values.
Variables are either global or local. Local variables are lexically scoped. They are in scope from the statement following their declaration up to the end of the block. If a name is not the name of a local variable it becomes a global variable reference. On your line 2, iter is a global variable.
Functions are not declared, they are values created when a function definition expression is executed. (A function defintion statement is simply an alternate syntax for a function definition expression and assignment to a variable.) Also, functions don't have names. They are simply referenced by one or more variables. So, your function value does exist and get referenced by the iter variable until execution control passes through the lines that contain the function definition. In your code, that's the end of line 11.
The factory / iterator function is implemented correctly. The problem is, by using
local function iter(to,from)
--...
end
is equivalent to:
local iter = function(to,from)
--...
end
iter is a local variable that fromto has no access to. Remove local and it will run.
--main.lua
for o = 1, 5 do
print(o)
SampleFunction()
end
--in B.lua
function SampleFunction()
print(o) --prints nil
end
I have a for loop, which is iterating over a range. Inside that for loop, I am calling a function which is in some other Lua file and I need the iterator variable to be accessible in the other Lua file, but it just prints NIL every time, what do I need to do?
Having a function use variables from its caller can be very brittle so its not something that Lua supports. What would happen if you called SampleFuncction from another place that doesn't have an o variable?
The way I would do this is to have have SampleFunction receive the loop index as a parameter
-- main.lua
for o = 1, 5 do
print(o)
SampleFunction(o)
end
--in B.lua
function SampleFunction(index)
print(index)
end
for i = 1, 5 do
o = i
SampleFunction()
end
The for-loop iterator is always locally-scoped to loop body, but you may assign it's value to any available global variable. Note that this is still not dynamic scoping -- you overwrite global o and this may interfere with other functions using that name.
I have a function that takes a variable amount of ints as arguments.
thisFunction(1,1,1,2,2,2,2,3,4,4,7,4,2)
this function was given in a framework and I'd rather not change the code of the function or the .lua it is from. So I want a function that repeats a number for me a certain amount of times so this is less repetitive. Something that could work like this and achieve what was done above
thisFunction(repeatNum(1,3),repeatNum(2,4),3,repeatNum(4,2),7,4,2)
is this possible in Lua? I'm even comfortable with something like this:
thisFunction(repeatNum(1,3,2,4,3,1,4,2,7,1,4,1,2,1))
I think you're stuck with something along the lines of your second proposed solution, i.e.
thisFunction(repeatNum(1,3,2,4,3,1,4,2,7,1,4,1,2,1))
because if you use a function that returns multiple values in the middle of a list, it's adjusted so that it only returns one value. However, at the end of a list, the function does not have its return values adjusted.
You can code repeatNum as follows. It's not optimized and there's no error-checking. This works in Lua 5.1. If you're using 5.2, you'll need to make adjustments.
function repeatNum(...)
local results = {}
local n = #{...}
for i = 1,n,2 do
local val = select(i, ...)
local reps = select(i+1, ...)
for j = 1,reps do
table.insert(results, val)
end
end
return unpack(results)
end
I don't have 5.2 installed on this computer, but I believe the only change you need is to replace unpack with table.unpack.
I realise this question has been answered, but I wondered from a readability point of view if using tables to mark the repeats would be clearer, of course it's probably far less efficient.
function repeatnum(...)
local i = 0
local t = {...}
local tblO = {}
for j,v in ipairs(t) do
if type(v) == 'table' then
for k = 1,v[2] do
i = i + 1
tblO[i] = v[1]
end
else
i = i + 1
tblO[i] = v
end
end
return unpack(tblO)
end
print(repeatnum({1,3},{2,4},3,{4,2},7,4,2))
I am trying to understand how CoffeeScript variables are scoped.
According to the documentation:
This behavior is effectively identical to Ruby's scope for local
variables.
However, I found out that it works differently.
In CoffeeScript
a = 1
changeValue = -> a = 3
changeValue()
console.log "a: #{a}" #This displays 3
In Ruby
a = 1
def f
a = 3
end
puts a #This displays 1
Can somebody explain it, please?
Ruby's local variables (starting with [a-z_]) are really local to the block they are declared in. So the Ruby behavior you posted is normal.
In your Coffee example, you have a closure referencing a. It's not a function declaration.
In your Ruby example, you don't have a closure but a function declaration. This is different. The Ruby equivalent to your Coffee is:
a = 1
changeValue = lambda do
a = 3
end
changeValue()
In closures, local variables present when the block is declared are still accessible when the block is executed. This is (one of the) powers of closures!
The a variable being used inside the changeValue function is the global a variable. That CoffeeScript will be compiled into the following JavaScript:
var a, changeValue;
a = 1;
changeValue = function() {
return a = 3;
};
changeValue();
console.log("a: " + a);
In order for changeValue to not alter the a variable (i.e. use a local variable), you would either need to have an argument to the function named a (which would create a as a variable local to that function) or declare a as a local variable inside the function using var a = 3; (not sure what the CoffeeScript for that would be, I'm not a CoffeeScript guy).
Some examples of JavaScript variable scope.