Как получить закрытие в Луа? - PullRequest
2 голосов
/ 22 мая 2009

предположим, у меня есть имя файла "test.lua", содержащее следующие строки:

--[[  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--]]

потому что loadstring делает свое дело в глобальной среде, поэтому, когда я звоню f_generate () () я получу сообщение об ошибке "попытка вызвать глобальное 'f' (нулевое значение)"

закомментированный код показывает, что функциональная среда не может справиться с этой проблемой.

причина - единственная структура данных в lua (и функциональная среда и многое другое реализуется таблицей), я думаю, разумно предположить, что замыкание также реализовано таблицей, но как я могу его получить?

Ответы [ 4 ]

4 голосов
/ 23 мая 2009

Исходя из поставленного вопроса и предоставленного примера кода, я не вижу необходимости использовать loadstring(), когда функции и замыкания являются первоклассными значениями в языке. Я хотел бы сделать это так:

-- 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

Мотивация яснее, если есть параметр для 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

Прохождение парсера с loadstring() явно выводит код за пределы вызова loadstring(). Локальные переменные не хранятся ни в одной таблице окружения. Они реализованы практически так же, как и на любом другом языке: хранилище для них выделяется генератором кода и недоступно вне этой компиляции. Конечно, модуль отладки (и API) может просматривать их, но это никогда не рекомендуется для использования вне отладчика.

Правильный способ сохранить ссылку на локальный объект для использования вне области видимости - это истинное значение до замыкания. Это то, что достигается fun = function() return f() end. В этом случае значение f сохраняется как значение функции, сохраненной в fun. Обратите внимание, что на практике это оборачивание как повышение стоимости довольно эффективно. Поиск имени не требуется, чтобы найти значение, и, поскольку я использовал хвостовой вызов, дополнительные стековые кадры также не требуются.

0 голосов
/ 24 августа 2009

Видите, вы не можете рассматривать функции / замыкания как таблицы. Рассмотрим следующий код:

local table = {
    baz = {
        blah = "bar"
    },
    foo = table.baz.blah
}

в этом случае вы выполняете эквивалент доступа к чему-то в более узкой области из более широкой области. Это невозможно с функциями, что означает, что если бы это было так, то вы могли бы получить доступ к локальным переменным, которые вы не могли нормально.

Теперь исправим ваш код:

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

Это позволит вам вызывать cmp для любых двух переменных с соответствующей функцией сравнения. Если я пропустил пункт о вашем коде, то, пожалуйста, скажите мне!

0 голосов
/ 22 мая 2009

Вы должны удалить local, иначе это будет сбор мусора.

--local f = function()
f = function()
  print"local function f in test.lua"
end
0 голосов
/ 22 мая 2009

Я думаю, что вы смешиваете две разные вещи:

  1. замыкания: для этого определение f () должно находиться внутри области действия любой локальной переменной, которую вы хотите заключить.

    • вставка локальных переменных в динамически скомпилированные функции. Официально это не поддерживается Lua. это не проблема окружающей среды, это вопрос масштаба.

Помните: область видимости является лексической, а среда - это то, что каждая функция считает «глобальным пространством».

Функция, созданная из текстовой строки, находится в другом лексическом пространстве, как если бы она находилась в другом файле, поэтому она имеет собственную область видимости, отделенную от других функций.

Кстати, интерфейс 'debug' позволяет вам взаимодействовать с локальными переменными функции, так что может быть способ. Я просто не чувствую необходимости делать это.

...