Немного о глобальных, локальных и нелокальных переменных (и окружениях)

Почти все работающиее с lua люди знают о глобальных и локальных переменных. Но всё равно я кратко их опишу. Глобальная переменная не требует определения, вообще-то все глоб. переменные можно найти в таблице _G  (она содержит сама себя). Т. е. правила обычные: по несуществующему индексу можно найти nil, можно задать метатаблицу, и всё такое. _G  является, как вы увидете, единственной общей переменной, и то не совсем.

Ну, с локальными переменными тоже вроде ясно: видны только в блоке, где были определены. К примеру:

А терерь, разберём такую ситуацию: функция передала другую функцию за свои пределы. И что же будет?

Не глобальная переменная, но сохраняется между вызовами? Это и есть нелокальная (upvalue) переменная. ОК, усложним пример:

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

lua evolution

Внимание! Описаные далее возможности относятся только к Lua версии 5.2  и выше! В более ранних версиях (включая Lua 5.1) это не будет работать. Убедитесь в свежести вашей Lua перед использованием данных возможностей.


Изменяя _G  мы меняем глобальное окружение, т. е. для всей программы (и загружаемых модулей тоже), что не всегда приносит желаемый эффект. На деле, с Lua 5.2 всё меняется (в лучшую сторону).

Примечание: Lua компилирует блок кода как анонимную функцию.

И на сцену выходит (барабанная дробь)… _ENV ! Да, по умолчанию _ENV == _G , и перезапись _G (типа _G = nil ) не скажется на работе программы (правда, если она сама не использует эту переменную). Если для блока кода _ENV не задана, то компилятор lua задаст её функции как upvalue равное ссылке на _G. Для примера, код

luac -l -l показывает так:

Можно проследить, что глобальное окружение здесь  0x1972370 , а таблица, которую мы создали — 0x1972640. Вы скорее всего заметили, что _ENV является upvalue для функций.
Также, компилятор преобразовал все свободные имена (то, что мы зовём глобальными переменными) в _ENV.varname.

Продолжение про окружения следует…

Добавить комментарий