Мне бы очень хотелось, чтобы вы назвали эти переменные более разумно. Итак, я буду:
local inner = 'asdf'
local function b()
return inner
end
inner = 10
return b
и
func = require 'test'
func()
Хорошо, теперь, когда мы знаем, о чем говорим, я могу продолжить.
Блок Lua test
имеет локальную переменную с именем inner
. В этом блоке вы создаете новую функцию b
. Так как это новая функция, она имеет область видимости в области чанка test
.
Поскольку он находится внутри функции, он имеет право доступа к локальным переменным, объявленным вне этой функции. Но поскольку он находится внутри функции, он не имеет доступа к этим переменным, как если бы он был одним из своих локальных элементов. Компилятор обнаруживает, что inner
является локальной переменной, объявленной вне области действия функции, поэтому он преобразует ее в то, что Lua называет «повышающим значением».
Функции в Lua могут иметь произвольное количество значений (до 255), связанных с ними, называемых «повышенными значениями». Функции, созданные в C / C ++, могут хранить некоторое количество повышений с помощью lua_pushcclosure
. Функции, созданные компилятором Lua, используют значения up для обеспечения лексической области видимости.
Область действия - это все, что происходит в фиксированном блоке кода Lua. Итак:
if(...) then
--yes
else
--no
end
Блок yes
имеет область действия, а блок no
имеет другую область. Любые переменные local
, объявленные в блоке yes
, не могут быть доступны из блока no
, поскольку они находятся вне области действия блока no
.
Конструкциями Lua, определяющими область действия, являются if/then/else/end
, while/do/end
, repeat/until
, do/end
, for/end
и function/end
. Кроме того, каждый скрипт, называемый Lua «чанк», имеет область действия.
Области являются вложенными. Из одной области вы можете получить доступ к локальным переменным, объявленным в более высокой области.
«Стек» представляет все переменные, объявленные как local
в определенной области видимости. Поэтому, если у вас нет локальных переменных в определенной области, стек для этой области пуст.
В C и C ++ знакомый вам «стек» - это просто указатель. Когда вы вызываете функцию, компилятор заранее определяет, сколько байт пространства требуется стеку функции. Он увеличивает указатель на эту сумму. Все переменные стека, используемые в функции, являются просто байтовыми смещениями от указателя стека. При выходе из функции указатель стека уменьшается на величину стека.
В Луа все по-другому. Стек для конкретной области видимости - это объект , а не просто указатель. Для любой конкретной области существует некоторое количество local
переменных, определенных для нее. Когда интерпретатор Lua входит в область действия, он «выделяет» стек размера, необходимого для доступа к этим локальным переменным. Все ссылки на локальные переменные просто смещаются в этот стек. Доступ к локальным переменным из более высоких областей (ранее определенных) просто обращается к другому объекту стека.
Так что в Lua у вас концептуально есть стек стеков (для ясности я буду называть его "s-stack"). Каждая область создает новый стек и выталкивает его, а когда вы покидаете область, он выталкивает стек из s-стека.
Когда компилятор Lua обнаруживает ссылку на переменную local
, он преобразует эту ссылку в индекс в s-стек, а смещение в этот конкретный стек. Таким образом, если он обращается к переменной в текущем локальном стеке, индекс в s-стеке относится к вершине s-стека, а смещение - это смещение в тот стек, где находится переменная.
Это нормально для большинства конструкций Lua, которые имеют доступ к областям действия. Но function/end
не просто создает новую область видимости; они создают новую функцию. И этой функции разрешен доступ к стекам, которые не являются только локальным стеком этой функции.
Стеки являются объектами.А в Lua объекты подлежат сборке мусора.Когда интерпретатор входит в область действия, он выделяет объект стека и выталкивает его.Пока объект стека помещается в s-стек, он не может быть уничтожен.Стек стеков относится к объекту.Однако, как только интерпретатор выходит из области видимости, он выталкивает стек из s-стека.Таким образом, поскольку на него больше нет ссылок, он подлежит сбору.
Однако функция, которая обращается к переменным вне своей локальной области видимости, все еще может ссылаться на этот стек.Когда компилятор Lua видит ссылку на переменную local
, которая не входит в локальную область функции, он изменяет функцию.Он выясняет, к какому стеку принадлежит локальная ссылка, на которую он ссылается, а затем сохраняет этот стек в качестве значения в функции.Он преобразует ссылку на эту переменную в смещение в это конкретное значение, а не смещение в стек, который в данный момент находится в s-стеке.
Так что, пока объект функции продолжает существовать, такжебудут стек (ы), на которые он ссылается.
Помните, что стеки динамически создаются и уничтожаются, когда интерпретатор Lua входит и выходит из области функций.Таким образом, если бы вы запускали test
дважды, вызывая loadfile
и выполняя возвращаемую функцию дважды, вы бы получили две отдельные функции, которые ссылаются на два отдельных стека .Ни одна из функций не увидит значение от другого.
Обратите внимание, что это может быть не совсем так, как это реализовано, но это общая идея, лежащая в основе.