Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
[Обучение] Основы Lua.docx
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
66.67 Кб
Скачать

Глобальный контекст

Все глобальные переменные являются полями обычной таблицы, называемой глобальным контекстом. Эта таблица доступна через глобальную переменную _G. Поскольку все глобальные переменные являются полями контекста, то _G._G == _G.

Глобальный контекст делает возможным доступ к глобальным переменным по динамически генерируемому имени:

val = _G[varname]

_G[varname] = val

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

local f = function (t,i)

return os.getenv(i)

end

setmetatable(_G, {__index=f})

Этот же прием позволяет запретить доступ к неинициализированным глобальным переменным.

Пакеты

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

my_package = {}

function my_package.foo()

...

end

Также можно делать все функции локальными и отдельно формировать таблицу экспортируемых функций:

local function foo() ... end

local function bar() ... end

my_package = {

foo = foo,

bar = bar,

}

Пакет загружается с помощью функции require(), причем во время загрузки имя, переданное этой функции (оно может не содержать расширения, которое добавляется автоматически) доступно через переменную _REQUIREDNAME:

if _REQUIREDNAME == nil then

run_some_internal_tests()

end

Классы и объекты

Конструкция tbl:func() (при объявлении функции и при ее вызове) предоставляет основные возможности, позволяющие работать с таблицей как с объектом. Основная проблема состоит в порождении многих объектов, имеющих сходное поведение, т.е. порожденных от одного класса:

function class()

cl = {}

cl.__index = cl -- cl будет использоваться как мета-таблица

return cl

end

function object( cl, obj )

obj = obj or {} -- возможно уже есть заполненные поля

setmetatable(obj, cl)

return obj

end

Здесь функция class создает пустую таблицу, подготовленную к тому, чтобы стать мета-таблицей объекта. Методы класса делаются полями этой таблицы т.е. класс является таблицей, одновременно содержащей методы объекта и его мета-методы. Функция object() создает объект заданного класса  —  таблицу, у которой в качестве мета-таблицы установлен заданный класс. Во втором аргументе может быть передана таблица, содержащая проинициализированные поля объекта.

Some_Class = class()

function Some_Class:foo() ... end

function Some_Class:new()

return object(self, { xxx = 12 })

end

x = Some_Class:new()

x:foo()

Наследование

В описанной реализации мета-таблица класса остается не использованной, что делает простой задачу реализации наследования. Класс-наследник создается как объект класса, после чего в нем устанавливающая поле __index таким образом, чтобы его можно было использовать как мета-таблицу:

function subclass( pcl )

cl = pcl:new() -- создаем экземпляр

cl.__index = cl -- и делаем его классом

return cl

end

Теперь в полученный класс-наследник можно добавить новые поля и методы:

Der_Class = subclass(Some_Class)

function Der_Class:new()

local obj = object(self, Some_Class:new())

obj.yyy = 13 -- добавляем новые поля

return obj

end

function Der_Class:bar() ... end -- и новые методы

y = Der_Class:new()

y:foo()

y:bar()

Единственный нетривиальный момент здесь состоит в использовании функции new() из класса-предка с последующей заменой мета-таблицы посредством вызова функции object().

При обращении к методам объекта класса-наследника в первую очередь происходит их поиск в мета-таблице т.е. в самом классе-наследнике. Если метод был унаследован, то этот поиск окажется неудачным и произойдет обращение к мета-таблице класса-наследника т.е. к классу-предку.

Основной недостаток приведенного общего решения состоит в невозможности передачи параметров в функцию new() класса-предка.