Reputation: 327
suppose i have a file name "test.lua" containing lines below:
--[[ test.lua --]]
local f = function()
print"local function f in test.lua"
end
f_generate = function()
local fun = loadstring(" f()")
-- local env = getfenv(1)
-- set(fun,env)
return fun
end
f_generate()()
--[[ end of test.lua--]]
because loadstring doing its stuff under the global environment, so when i call f_generate()() i will get an error "attempt to call global 'f' (a nil value)"
the code commented out shows that function environment can't deal with this problem.
cause table is the only data structure in lua, (and function environment and other lots of thing are implement by table), i think is reasonable to assume that the closure are also implement by table, but how can i get it?
Upvotes: 2
Views: 3440
Reputation: 39467
See, you can't treat functions/closures as tables. Consider the following code:
local table = {
baz = {
blah = "bar"
},
foo = table.baz.blah
}
in this case, you're performing the equivalent of accessing something in a narrower scope from a wider scope. This is not possible with functions, which means that if this was true then you could access local variables that you couldn't normally.
Now, fixing your code:
local __cmp__table = {
[">"] = function(a,b) return a>b end,
[">="] = function(a,b) return a>=b end,
["<"] = function(a,b) return a<b end,
["<="] = function(a,b) return a<=b end,
["=="] = function(a,b) return a==b end,
["~="] = function(a,b) return a~=b end,
}
cmp = function(a, op, b)
return __cmp__table[op](a,b)
end
This will allow you to call cmp on any two variables with the proper comparison function. If i missed the point about your code, then please tell me!
Upvotes: 0
Reputation: 43366
From the question as asked and the sample code supplied, I don't see any need for using loadstring()
when functions and closures are first-class values in the language. I would consider doing it like this:
-- test.lua local f = function() print"local function f in test.lua" end f_generate = function() local fun = function() return f() end return fun end f_generate()() -- end of test.lua
The motivation is clearer if there is a parameter to f_generate:
-- test.lua local f = function(y) print("local function f("..y..") in test.lua") end f_generate = function(name) local fun = function() return f(name) end return fun end f_generate("foo")() f_generate("bar")() -- end of test.lua
Going through the parser with loadstring()
explicitly takes the code outside the scope of the call to loadstring()
. Local variables are not stored in any environment table. They are implemented much the same way they would be in any other language: storage for them is allocated by the code generator and is not accessible outside that compilation. Of course, the debug module (and API) has the ability to peek at them, but that is never recommended for use outside of a debugger.
The correct way to preserve a reference to a local for use outside the scope is as a true upvalue to a closure. That is what is achieved by fun = function() return f() end
. In that case, the value f
is retained as an upvalue to the function stored in fun
. Note that in practice, this wrapping as an upvalue is quite efficient. No name lookup is needed to find the value, and since I used a tail-call, no extra stack frames are required either.
Upvotes: 4
Reputation: 43160
You must remove 'local' otherwise it will be garbage collected.
--local f = function()
f = function()
print"local function f in test.lua"
end
Upvotes: 0
Reputation: 62671
I think you're mixing two different things:
closures: for this, the definition of f() should be inside the scope of whatever local variable you want to enclose.
Remember: scope is lexical, while environment is what each function considers "global space".
A function constructed from a text string is in a different lexical space, just as if it were in a different file, therefore it has it's own scope, separated from the other functions.
BTW, the 'debug' interface let's you meedle with the local variables of a function, so there might be a way. I just haven't feel the need to do this.
Upvotes: 0