tags:

views:

756

answers:

4

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?

A: 

I think you're mixing two different things:

  1. closures: for this, the definition of f() should be inside the scope of whatever local variable you want to enclose.

  2. inserting local variables into dynamically compiled functions. this isn't formally supported by Lua. it's not an environment issue, it's a scope thing.

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.

Javier
A: 

You must remove 'local' otherwise it will be garbage collected.

--local f = function()
f = function()
  print"local function f in test.lua"
end
Nick D
+4  A: 

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.

RBerteig
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,}local __cmp__ = function(a, op, b) return __cmp__table[op](a,b)endlocal env = { __cmp__ = __cmp__ ,}local checkMember = function(member) local f = loadstring([[ -- a, op, b = ... a = a.]] .. member .. " return __cmp__(a, op, b)" ) setfenv(f,env) return fend
gray
this is the real code, it seems that i really need loadstring,any suggustion?and put aside all the practical need and workaround, we can still talk about the ability to get the closure;after all, the closure is the function's active record chain(correct me if ...);and i think lua implement it as table(because all thing can implement as table...;again,correct me if...)thanks a lot, it really helps.
gray
@gray: no, closures and local variable frames aren't tables.
Javier
A: 

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!

RCIX