Приветы!
Я начал скриптовать на луа примерно 10 лет назад (9 с копейками, но для красоты округлим).
В данном посте решил затронуть несколько продвинутых тем по луа, которые не так очевидны, и возможно неизвестны даже для "уверенных луа скриптеров".
Погнали!
Для начала, давайте возьмем за тезис - луа основана на таблицах. Не верите? Ну, ладно, давайте не будем брать за тесис, а проверим.
Все глобальные таблицы, функции, переменные - все это попадает в одну общую таблицу _G.
Локальные вещи (те, что local) - они работаю чуть иначе, и работать с ними можно только через библиотеку debug (не нужно!).
Набросал маленький быдлокод, который печатает содержимое таблицы _G:
Запустить можно в Муне, либо через командную строку исполнить lua файл.
Третий вариант: dofile("путь") в LUA CLI.
Окей, таблица _G, поняли, дальше-то что? Ну, вообще удобно, чтобы получать доступ к глобальным элементам через строкове имя, например:
Дальше поговорим о мета таблицах...
У каждой таблицы может быть так называемая метатаблица, которая может содержать специальные поля, задавая значения которым мы можем менять поведение объектов (таблицы - это объекты по своей сути).
Например с помощью метатаблиц мы можем изменить логику действия арифметических и логических операторов для определнного объекта.
Давайте ближе к примерам:
Собственно, из кода, думаю, все понятно. Если нет - пишите вопросы, но если вы не знаете луа, то все описанное в этой статье вам не нужно. А если знаете - должны понять по коду.
Данные поля, к слову, именуются метаметодами.
Список других зарезервированных метаметодов:
При любой из этих операций сначала проверяется наличие перегруженного оператора у первого объекта, потом у второго, если нету - ошибка.
Первый это тот, который написан первым a + b — тут a первый >< ))
Отдельно хочу заметить метаметод __index, потому что он особенно интересен.
Реализация выглядит вот так:
Данный метамод вызывается, когда мы пытаемся запросить несуществующий ключ в таблице. Если в метатаблице определен данный метод - будет возвращен его результат, иначе - nil.
Уникальность заключается еще и в том, что данному метаметоду не обязательно быть функцией, в отличие от всех вышеперечисленных. Мы можем задать данному метаметоду таблицу, и тогда он будет автоматический возвращать значение из нее.
Пример выше разумеется нужно совместить с прошлым.
Удобно применять для реализации "базовых" классов (таблиц, объектов),
Так же существует метаметод __newindex, вызывается при записи нового индекса в таблицу (объект, ага, уже сам не знаю как их называть в итоге).
Выглядит следующим образом:
Как и __index можно прировнять таблице, тогда данные будут автоматический записываться в нее.
Ничего не должен возвращать, можно применять для создания readonly таблиц, или обновления сразу в нескольких местах. Или для статических переменных класса - луа дает неограниченные возможности для вашей больной фантазии.
В конце немного про вызов методов через ":".
В луа есть возможность автоматический передавать ссылку на текущую таблицу, при записи метода определенным образом, а именно:
Т.е. вызвая функцию через ":" в нее автоматический передается параметр self, который содержит в себе таблицу, содержащую вызываемый метод.
Параметр self можно записывать явно в аргументах метода, а можно и не записывать. Пример выше мог выглядеть вот так:
Вызывая метод через точку нам нужно самим передавать таблицу как аргумент:
Небольшой общий пример:
На этом пока все, прошу прощения за сумбурность изложения, возможно в скором времени причешу данный пост, писал урывками на работе, ушло аж 3 дня по 10 минут %)
Больше времени пока нету.
Пишите вопросы, отвечу!
UPD: поправил табуляцию
Я начал скриптовать на луа примерно 10 лет назад (9 с копейками, но для красоты округлим).
В данном посте решил затронуть несколько продвинутых тем по луа, которые не так очевидны, и возможно неизвестны даже для "уверенных луа скриптеров".
Погнали!
FYP написал(а):Нихуя вы не знаете, но я вас научу!
Для начала, давайте возьмем за тезис - луа основана на таблицах. Не верите? Ну, ладно, давайте не будем брать за тесис, а проверим.
Все глобальные таблицы, функции, переменные - все это попадает в одну общую таблицу _G.
Локальные вещи (те, что local) - они работаю чуть иначе, и работать с ними можно только через библиотеку debug (не нужно!).
Набросал маленький быдлокод, который печатает содержимое таблицы _G:
Lua:
local printed = {};
local function has_value (tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
function printTable(t, indent)
for k, v in pairs(t) do
if not has_value(printed, v) then
if type(v) == "table" then
table.insert(printed, v);
print(string.rep(".", indent * 4) .. "[" .. k .. "] (T:" .. type(v) .. ") => {")
printTable(v, indent + 1);
print(string.rep(".", indent * 4) .. "}")
else
print(string.rep(".", indent * 4) .. "[" .. k .. "] (T:" .. type(v) .. ") => " .. tostring(v))
end
end
end
end
print("*************** _G table structure: ***************")
printTable(_G, 0);
print("*************** ok we done :)) ***************")
Запустить можно в Муне, либо через командную строку исполнить lua файл.
Третий вариант: dofile("путь") в LUA CLI.
Окей, таблица _G, поняли, дальше-то что? Ну, вообще удобно, чтобы получать доступ к глобальным элементам через строкове имя, например:
Lua:
local toStr = "tostring";
print(_G[toStr]("HAHA"));
Дальше поговорим о мета таблицах...
У каждой таблицы может быть так называемая метатаблица, которая может содержать специальные поля, задавая значения которым мы можем менять поведение объектов (таблицы - это объекты по своей сути).
Например с помощью метатаблиц мы можем изменить логику действия арифметических и логических операторов для определнного объекта.
Давайте ближе к примерам:
Lua:
-- В данном случае создаем "класс" cool
local cool = {};
-- Внутри класса cool создаем новую таблицу mt, которая будет являться метатаблицей.
cool.mt = {};
-- создаем внутри таблицы mt метод с именем __add, который переопределяет поведение операции сложения (+)
function cool.mt.__add(a, b)
return (a.somefield + b.somefield) * 2;
end
-- cool.new() -> метод конструктор нашего крутого "класса" cool.
function cool.new(somefieldValue)
-- Создаем новую таблицу
local newCool = {};
-- Задаем полю somefield аргумент который прислали в функцию.
newCool.somefield = somefieldValue;
-- Задаем вновьсозданной таблице метатаблицу cool.mt
setmetatable(newCool, cool.mt);
-- Возвращаем вновьсозданную таблицу.
return newCool;
end
-- Создаем 2 инстанции нашего "крутого" класса передавая параметром в конструктор разные аргументы.
local intanceOfCoolClass = cool.new(2);
local secondIntanceOfCoolClass = cool.new(3);
-- проверим, что конструктор сработал:
print("---- Class values: ----");
print("Class 1: " .. intanceOfCoolClass.somefield);
print("Class 2: " .. secondIntanceOfCoolClass.somefield);
print("Class 1 metatable: " .. tostring(getmetatable(intanceOfCoolClass)));
print("Class 2 metatable: " .. tostring(getmetatable(intanceOfCoolClass)));
print("WOW TABLES EQUALS!!11");
print("---- End of test stuff ----");
-- Как видим - значения разные, метатаблица одна (получать можно через getmetatable)
-- Терь проверим перегрузку сложение (метода +)
-- У нас там (somefield + somefield) * 2, т.е. должно получится (2 + 3) * 2 => 10.
print("Result: " .. intanceOfCoolClass + secondIntanceOfCoolClass);
-- Ого, мы крутые!
Собственно, из кода, думаю, все понятно. Если нет - пишите вопросы, но если вы не знаете луа, то все описанное в этой статье вам не нужно. А если знаете - должны понять по коду.
Данные поля, к слову, именуются метаметодами.
Список других зарезервированных метаметодов:
Код:
__add -> +
__sub -> -
__mul -> *
__div -> /
__unm -> ~ или -
__pow -> ^
__concat -> ..
_eq -> ==
__lt -> <
__le -> <=
__tostring -> метаметод, который вызывается функцией tostring()
При любой из этих операций сначала проверяется наличие перегруженного оператора у первого объекта, потом у второго, если нету - ошибка.
Первый это тот, который написан первым a + b — тут a первый >< ))
Отдельно хочу заметить метаметод __index, потому что он особенно интересен.
Реализация выглядит вот так:
Lua:
cool.mt.__index = function (table, key)
return "Key " .. key .. " not found!";
end
Данный метамод вызывается, когда мы пытаемся запросить несуществующий ключ в таблице. Если в метатаблице определен данный метод - будет возвращен его результат, иначе - nil.
Уникальность заключается еще и в том, что данному метаметоду не обязательно быть функцией, в отличие от всех вышеперечисленных. Мы можем задать данному метаметоду таблицу, и тогда он будет автоматический возвращать значение из нее.
Lua:
local coolDefaultValues = {
somefield2 = -1337
}
cool.mt.__index = coolDefaultValues;
print(cool.new(123).somefield2)
Пример выше разумеется нужно совместить с прошлым.
Удобно применять для реализации "базовых" классов (таблиц, объектов),
Так же существует метаметод __newindex, вызывается при записи нового индекса в таблицу (объект, ага, уже сам не знаю как их называть в итоге).
Выглядит следующим образом:
Lua:
cool.mt.__newindex = function (t,k,v)
{
print("Table: " .. t .. " | Key: " .. k .. " | Value: " .. tostring(v));
}
Как и __index можно прировнять таблице, тогда данные будут автоматический записываться в нее.
Ничего не должен возвращать, можно применять для создания readonly таблиц, или обновления сразу в нескольких местах. Или для статических переменных класса - луа дает неограниченные возможности для вашей больной фантазии.
В конце немного про вызов методов через ":".
В луа есть возможность автоматический передавать ссылку на текущую таблицу, при записи метода определенным образом, а именно:
Lua:
local myTable = {}
myTable.bhack = "1337";
myTable:SomeMethod()
{
-- self == myTable;
print(self.bhack);
}
myTable:SomeMethod();
Т.е. вызвая функцию через ":" в нее автоматический передается параметр self, который содержит в себе таблицу, содержащую вызываемый метод.
Параметр self можно записывать явно в аргументах метода, а можно и не записывать. Пример выше мог выглядеть вот так:
Lua:
local myTable = {}
myTable.bhack = "1337";
myTable:SomeMethod(self)
{
-- self == myTable;
print(self.bhack);
}
myTable:SomeMethod();
Вызывая метод через точку нам нужно самим передавать таблицу как аргумент:
Lua:
local myTable = {}
myTable.bhack = "1337";
myTable:SomeMethod(self)
{
-- self == myTable;
print(self.bhack);
}
myTable.SomeMethod(myTable);
Небольшой общий пример:
Lua:
local meCool = {};
meCool.DB = {};
function printTable(t, indent)
for k, v in pairs(t) do
if type(v) == "table" then
table.insert(printed, v);
print(string.rep(".", indent * 4) .. "[" .. k .. "] (T:" .. type(v) .. ") => {")
printTable(v, indent + 1);
print(string.rep(".", indent * 4) .. "}")
else
print(string.rep(".", indent * 4) .. "[" .. k .. "] (T:" .. type(v) .. ") => " .. tostring(v))
end
end
end
function meCool.DB:SomeMethod()
print("***** content of self: *****");
printTable(self, 0);
print("***** end of self: *****");
self.SomeMethod2();
end
function meCool.DB:SomeMethod2()
print("ya i am here!");
end
function meCool.DB.SomeMethod3(self, exampleVar)
print("***** content of self: *****");
printTable(self, 0);
print("***** end of self: *****");
print("Btw var is: " .. exampleVar);
end
meCool.DB:SomeMethod();
meCool.DB:SomeMethod3("woah!");
meCool.DB.SomeMethod3(meCool.DB, "woah2!");
На этом пока все, прошу прощения за сумбурность изложения, возможно в скором времени причешу данный пост, писал урывками на работе, ушло аж 3 дня по 10 минут %)
Больше времени пока нету.
Пишите вопросы, отвечу!
UPD: поправил табуляцию
Последнее редактирование: