Ktlo 789 Опубликовано: 24 августа, 2015 (изменено) Метатаблицы — это самые обыкновенные таблицы, содержащие функции, которые изменяют стандартное поведение операторов в Lua. Метатаблица в Lua может быть у любого значения, но только у каждой таблицы может быть своя метатаблица, у всех остальных же одна на всех. Так например у типа значения строки есть своя встроенная метатаблица, которая позволяет нам делать так: ("строка"):rep(10) В данной таблице присутствует поле __index. А у потоков метатаблицы нет, а надо бы!!! Но обо всём по порядку.Для установки и получения метатаблиц в Lua по стандарту существует 4 функции: setmetatable(table: table, metatable: table):table — это основная функция, которая позволяет установить таблице метатаблицу. Такая функция работает только с таблицами. getmetatable(value: value):table or value — возвращает метатаблицу, если есть или значение поля __metatable в метатаблице, если есть. Работает со всеми значениями. debug.setmetatable(value: value):boolean or table — устанавливает метатаблицу любому значению и обходит метаполе __metatable. Действительно мощная штука, вот почему её убрали в Open Computers . debug.getmetatable(value: value):table or value — возвращает метатаблицу, обходя поле __metatable. Также отсутствует в OC. Теперь, я думаю, можно приступить к самим метаметодам. Для своего, а может быть и вашего удобства я их разделю на четыре категории:Разное Начну сразу с этой категории, т.к. здесь всё самое вкусное. 1) __index(self: value, key: value) — метаметод, который вызывается при попытке индексирования значения с этим метаметодом. В качестве аргументов функция получает само значение, как первый аргумент, и индекс как второй, возвращать должна любое значение под заданным индексом. Если значение-таблица, то вызывается только тогда, когда нет поля с искомым индексом в самой таблице. Если вам необходимо проигнорировать этот метаметод, используйте rawget(table: table, key: value):value, эта простая функция вернёт значение в самой таблице и не притронется к метаметоду. Пример: local t = { } setmetatable(t, { __index = _G }) t:print() --то же самое с помощью функции t = setmetatable(t, { __index = function(self, key) return _G[key] end }) t:print() 2) __newindex(self: value, key: value, value: value) — делает то же что и __index, только наоборот. Этот метаметод вызывается, когда значению, присваивают что-нибудь под каким-нибудь индексом. Также можно установить вместо функции таблицу, куда будут уходить все новые значения под новыми индексами. Если ваше значение с метатаблицей — таблица, то вы можете вставить поле прямо в неё, избегая метометод, с помощью rawset(table: table, key: value, value: value):nil. Пример: --Давайте сделаем таблицу, которую нельзя обойти стандартными итераторами local t = { } local private = { } setmetatable(t, { __index = private; __newindex = private; }) t.var = 23 t.var2 = 42 for key, value in pairs(t) do --По сути ничего неделает print(key, value) end print(t.var) --> 23 print(t.var2) --> 42 3) __call(self: value, args: ... ) — превращает любое значение с метатаблицей в типа функцию. С ним вы можете вызвать ваше значение, как функцию. Первым аргументом всегда будет само значение, остальные — это те которые были указаны при вызове функции. Пример: local t = { } setmetatable(t, { __call = function(self, String) table.insert(self, String) return true end }) print(t("Hello!")) --> true print(t[1]) --> Hello! 4) __metatable: value — это не метаметод, это просто поле, которое содержит информацию, которая будет возвращаться функцией getmetatable(value: value):value. Также при наличии этого поля не удастся сменить метатаблицу с помощью setmetatable(value: value, metatable: table):table на другую: выскочит ошибка. Чтобы проигнорировать этот параметр можно использовать функции из библиотеки debug. 5) __tostring(self: value) — при попытке перевести значение в строку будет вызван этот метаметод, использован же будет его результат. Пример: local t = { } setmetatable(t, { __tostring = function(self) return "Я - таблица!" end }) print(tostring(t)) --> Я - таблица! print(t) --Да, да. Это также работает. --> Я - таблица! 6) __len(self: value) — взаимодействует с оператором #. Если вы хотите проигнорировать этот метаметод в значении таблица, просто используйте table.getn(table: table):number. Не знаю, что добавить, смотрите пример. Пример: local t = { } setmetatable(t, { __len = function(self) return 3 --Размер этого "массива" теперь всегда 3 end }) for i = 1, 8 do table.insert(t, i) end -- В массиве теперь 8 элементов, print(#t) -- но результат всё равно 3 --> 3 7) __pairs(self: value) — заменяет собой функцию pairs(table: table), должен возвращать итератор, как минимум. Проигнорировать можно, подставив стандартный итератор next(level: number, table: table):value, value. 8) __ipairs(self: value) — то же самое, но для ipairs(table: table). Стандартный итератор — __inext(level: number, table: table):value, value (не удивляйтесь двойному подчёркиванию, это не метаметод, а стандартная функция глобального окружения). 9) __name: string — метаполе, которое используется при возникновении ошибки. Это поле в метатаблице наверняка существует для типа userdata, но также успешно работает с таблицами. Когда срабатывает ошибка несоответствия типов данных, то Lua заменяет слово, обозначающее этот тип данных, на то, что хранится в __name (при условии, что __name — строка). Для понимания того, что вы сейчас прочитали, пример: t = setmetatable({}, { __name = "qwerty" }) -- Переименовываем table в qwerty print(type(t)) -- Никак не влияет на type --> table math.sqrt(t) -- t - не число, а из таблицы корень не получишь --[[ Этот код выдаст примерно следующую ошибку: stdin:2: bad argument #1 to 'sqrt' (number expected, got qwerty) ]] Следующие два метаметода взаимодействуют со сборщиком мусора в Lua. На разных версиях lua они немного отличаются по возможностям. Я опишу, как они работают на lua 5.3. 10) __gc(self: value) — вызывается, когда сборщик мусора хочет удалить объект из памяти. По некоторым данным не работает с таблицами, но я протестировал в Lua интерпритаторе, и всё вроде как функционирует . Я думаю, что этот метаметод может заменить собой деструктор "класса" в Lua. Пример: setmetatable({ }, { __gc = function(self) print(self, " был удалён с ОЗУ") end }) --[[ Через какое-то время ленивый сборщик мусора удалит вашу таблицу, и вы увидите этот знаменательный момент]] 11) __mode: string — параметр, указывающий на то, что таблица не может защитить ключи или значения от безжалостного сборщика мусора. Если __mode == "k", то при не нахождении переменной содержащей, значение равное этому ключу, сборщик мусора спокойно удалит это поле и из таблицы тоже, если же __mode == "v", то тоже самое он будет делать для значений в таблице. Полностью правильно это будет работать только если удаляемые ключи и значения не являются: строкой, boolean или числом (это связано с особенностью работы сборщика). Пример: local t = setmetatable({ }, { __mode = "v" }) do local var = {} t.var = var end print(t.var) --> table: XXXXXXXXXXX -- После очередного сбора мусора print(t.var) --> nil Математические 1) __unm(self: value) — унарный минус, это, когда var = -value. В качестве аргумента получает само значение. Думаю, примеры здесь не нужны. Следующие метаметоды однотипны, поэтому я опишу принцип действия для всех, а потом с какими операторами они взаимодействуют. Принцип действия: сначала Lua смотрит на первое значение в действии, если оно содержит метатаблицу с метаметодом этого действия, то он вызывается, и решением этого действия становится результат метаметода (то, что он вернёт). Если не находит смотрит во втором значении и проделывает тоже самое, если ненаходит ни там ни там, то действует по стандартному алгоритму. При постановке аргументов в метаметоде использует их изначальный порядок (то есть 2 + 3 будет вызывать __add(2, 3)) Надеюсь объяснил понятно. 2) __add(op1: value, op2: value) — операция сложения. Оператор +. 3) __sub(op1: value, op2: value) — операция вычитания. Оператор -. 4) __mul(op1: value, op2: value) — операция умножения. Оператор *. 5) __div(op1: value, op2: value) — операция деления. Оператор /. 6) __mod(op1: value, op2: value) — операция получения остатка от деления. Оператор %. 7) __pow(op1: value, op2: value) — операция возведения в степень. Оператор ^. Игнорируется функцией math.pow(op1: number, op2: number):number. 8) __concat(op1: value, op2: value) — операция конкатенации (склейки). Оператор ... Игнорируется функцией table.concat(table: table[, insert_string: string, begin: number, end: number]):string. 9) __idiv(op1: value, op2: value) — операция целочисленного деления. Оператор //. Появился в Lua 5.3. Пример один на всех, но только с операцией сложения: local _42 = { } setmetatable(_42, { __add = function(op1, op2) op1 = type(op1) == "table" and 42 or op1 --Если первый аргумент - таблица, то присваиваем ему значение 42, иначе его изначальное значение op2 = type(op2) == "table" and 42 or op2 --Проделываем то же самое return op1 + op2 --Складываем и возвращаем результат end }) print(_42 + 8) --> 50 print(8 + _42) --> 50 print(_42 + _42) --> 84 print({} + _42) --> 84 Немного странный результат у последнего примера, правда? Попробуйте это объяснить. Сравнение Следующие метаметоды нотличаются от всех тех, что выше. Во-первых они могут возвращать только логическое значение (true или false), если возвращаемое значение не является таковым, то будет переделано в него (например "return {}" будет восприниматься, как "return true"). Во-вторых, они будут вызываться только, если одинаковый метаметод присутствует в метатаблицах обоих значений, причём сами метатаблицы могут быть разными. 1) __eq(op1: value, op2: value) — операция равенства, операторы: == и ~=. При использовании оператора ~= возвращаемое значение инвертируется. Порядок сравниваемых значений в аргументах соответствует их изначальной постановке. Не вызывается если значения одинаковы (например, когда мы сравниваем одну и ту же таблицу). Пример: local t1 = {} local t2 = {} local __eq = function(op1, op2) print "Работает" return op1 end setmetatable(t1, { __eq = __eq }) setmetatable(t2, { __eq = function(op1, op2) print "Работает" return op1 end }) print(t1 == t2) --__eq не вызвался, т.к. методы в метатаблицах разные. --> false setmetatable(t2, { __eq = __eq }) print(t1 == t2) --Метод успешно вызван --> Работает --> true print(t1 == t1) --Не вызван, т.к. одинаковы --> true 2) __lt(op1: value, op2: value) — операция меньше чем, операторы: < и >. Сравниваемые значения расставляются в аргументах в том порядке, в каком их сравнивают, при использовании оператора <. Оператор > просто меняет их местами. 3) __le(op1: value, op2: value) — операция меньше или равно, операторы: <= и >=. Работает также, что и метаметод выше, но с другими операторами. Битовые операторы В Lua 5.3 появились новые операторы (о которых я даже не подозревал). Эти операторы позволяют производить побитовые операции над целыми числами. Им также соответствуют некоторые специальные поля в метатаблице. В предыдущих версиях Lua для этого использовалась отдельная стандартная библиотека bit32. Все соответствующие операторам функции в библиотеке можно использовать для обхода метаметодов в метатаблице. 1) __band(op1: value, op2: value) — операция побитового и. Оператор &. 2) __bor(op1: value, op2: value) — операция побитового или. Оператор |. 3) __bxor(op1: value, op2: value) — операция побитового исключающего или. Оператор ~ (бинарная версия). 4) __bnot(self: value) — операция побитового не. Оператор ~ (унарная версия). 5) __shl(op1: value, op2: value) — операция битового сдвига влево. Оператор <<. 6) __shr(op1: value, op2: value) — операция битового сдвига вправо. Оператор >>. Пример с операторами <<: local meta = getmetatable(io.stdout) function meta.__shl(ostream, data) return ostream:write(tostring(data)) end local cout = io.stdout local endl = "\n" cout = cout << "Я люблю C++!" << endl << "Число: " << math.pi << endl << cout; Изменено 28 марта, 2016 пользователем Ktlo 11 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Ktlo Автор темы 789 Опубликовано: 25 августа, 2015 Обновил и добавил про математические метаметоды. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
swg2you 403 Опубликовано: 26 августа, 2015 Немного странный результат у последнего примера, правда? Попробуйте это объяснить. --Если первый аргумент - таблица, то присваиваем ему значение 42, иначе его изначальное значение Шутишь? --- Ме-та-та-бли-бли-бли-цы - уж-жасное название. Еще ужаснее конструкции вида: при работе с метатаблицами, она устанавливает таблице метатаблицу. Первый аргумент здесь — таблица, второй — метатаблица Прочитал, теперь заикаюсь. Материал хорош, и грамотен, но та-та-та-блицы с ме-ме-ме-та-методами нужно подавать проще и другими словами. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
davial 1 972 Опубликовано: 26 августа, 2015 (изменено) Шутишь? --- Ме-та-та-бли-бли-бли-цы - уж-жасное название. Еще ужаснее конструкции вида: Прочитал, теперь заикаюсь. Материал хорош, и грамотен, но та-та-та-блицы с ме-ме-ме-та-методами нужно подавать проще и другими словами. Оно вроде как и .. ты прав ... но ... из песни слов не выкинешь. Чем думаешь заменить валийское metatable.Каким словом из русского языка? Чтобы звучало - просто, понятно и правильно. Изменено 26 августа, 2015 пользователем davial Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
LeshaInc 625 Опубликовано: 26 августа, 2015 Оно вроде как и .. ты прав ... но ... из песни слов не выкинешь. Чем думаешь заменить валийское metatable.Каким словом из русского языка? Чтобы звучало - просто, понятно и правильно.о-себе-таблица))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Totoro 3 563 Опубликовано: 26 августа, 2015 (изменено) Да, термин "таблица" рябит в глазах, особенно если это замечаешь. Однако, на мой взгляд, в статье на подобную тему это неизбежно. И уж тем более не стоит заменять термин "метатаблица" на неологизм собственного сочинения, вводя в заблуждение читающих статью новичков. setmetatable(table, table) — эта основная функция при работе с метатаблицами, она устанавливает таблице метатаблицу. Первый аргумент здесь — таблица, второй — метатаблица. Такая функция работает только с таблицами. Можно использовать синтаксис описания команд, подобный тому, что можно увидеть на http://ocdoc.cil.li/ И переформулировать фразы немного, чтобы избежать излишней тавтологии. setmetatable(table: table, metatable: table) — это основная функция, которая позволяет установить таблице метатаблицу. Т.е. тип аргументов и какой идет куда, становится понятно из сигнатуры. Изменено 26 августа, 2015 пользователем Totoro Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy 2 187 Опубликовано: 26 августа, 2015 Наверное следует упомянуть, что __index и __newindex могут быть не только методами, но и таблицами. На счет остальных заклинаний не уверен. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Ktlo Автор темы 789 Опубликовано: 26 августа, 2015 Наверное следует упомянуть, что __index и __newindex могут быть не только методами, но и таблицами. На счет остальных заклинаний не уверен. Но я упоминал об этом. Да, термин "таблица" рябит в глазах, особенно если это замечаешь. Однако, на мой взгляд, в статье на подобную тему это неизбежно. И уж тем более не стоит заменять термин "метатаблица" на неологизм собственного сочинения, вводя в заблуждение читающих статью новичков. Можно использовать синтаксис описания команд, подобный тому, что можно увидеть на http://ocdoc.cil.li/ И переформулировать фразы немного, чтобы избежать излишней тавтологии. Т.е. тип аргументов и какой идет куда, становится понятно из сигнатуры. Переделаю, так действительно будет лучше. 3 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
swg2you 403 Опубликовано: 27 августа, 2015 (изменено) Однако, на мой взгляд, в статье на подобную тему это неизбежно. И уж тем более не стоит заменять термин "метатаблица" на неологизм собственного сочинения, вводя в заблуждение читающих статью новичков. Тут как раз "метатаблица" и есть неблагозвучный калькированный с английского неологизм. Если бы документацию писали, а не переводили, то термин скорее всего был бы "суб-таблица" или составной, но более точный по сути "таблица операторов". А в случае с __index и __newindex было бы оговорено, что в качестве таблицы операторных методов может выступать обычная таблица индексов-значений. ЗЫ "метаметод" - тоже, еще та корявость ) мета-метод это как? Изменено 27 августа, 2015 пользователем swg2you Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Totoro 3 563 Опубликовано: 27 августа, 2015 Тут как раз "метатаблица" и есть неблагозвучный калькированный с английского неологизм. Если бы документацию писали, а не переводили, то термин скорее всего был бы "суб-таблица" или составной, но более точный по сути "таблица операторов". Немного не соглашусь. У неологизма "метатаблица" есть как минимум пара преимуществ, по сравнению с другими вариантами. Первое - он широко распространен (в контексте Луа). Как в английском так и в русском языке. Второе - это наиболее точный перевод термина "metatable" на русский. То что перевод так похож на оригинал - лишь облегчает понимание мануалов. Так что тут уже все решено за нас создателями языка. Термин "суб-таблица" возможно прижился бы. Но он звучит менее определенно. "Под-таблица". Это таблица-элемент? Таблица операторов? Или что-то еще? Приставка "мета-" носит в русском языке (хотя она несомненно иностранного происхождения) смысл абстрактности и обобщенности. Что вполне подходит к случаю, на мой взгляд. 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
swg2you 403 Опубликовано: 27 августа, 2015 Немного не соглашусь. У неологизма "метатаблица" есть как минимум пара преимуществ, по сравнению с другими вариантами. Первое - он широко распространен (в контексте Луа). Как в английском так и в русском языке. Второе - это наиболее точный перевод термина "metatable" на русский. То что перевод так похож на оригинал - лишь облегчает понимание мануалов. Так что тут уже все решено за нас создателями языка. Термин "суб-таблица" возможно прижился бы. Но он звучит менее определенно. "Под-таблица". Это таблица-элемент? Таблица операторов? Или что-то еще? Приставка "мета-" носит в русском языке (хотя она несомненно иностранного происхождения) смысл абстрактности и обобщенности. Что вполне подходит к случаю, на мой взгляд. мета- из греческого "рядом с" перекочевало как приставка в английский и русский, и укоренилось в узких значениях: на другом уровне, выше, ниже, после, между. Мета-фора, мета-язык, мета-физика, и т.п. А насчет распространенности это да. Мы любим кальку. Большая часть слов в современном Русском - заимствованы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Ktlo Автор темы 789 Опубликовано: 27 августа, 2015 Обновил и добавил про метаметоды сравнения. Возможно потом, что-то ещё буду дописывать, но скорее нет. Вроде описал все поля, используемые в метатаблицах. Но если что-то забыл, пишите. 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
SergOmarov 34 Опубликовано: 29 августа, 2015 Наверное следует упомянуть, что __index и __newindex могут быть не только методами, но и таблицами. На счет остальных заклинаний не уверен. Еще может быть __call, если ее саму можно вызвать Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Ktlo Автор темы 789 Опубликовано: 29 августа, 2015 Еще может быть __call, если ее саму можно вызвать Нет, я проверил в Lua интерпритаторе, и твоё предположение на счёт __call не работает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
SergOmarov 34 Опубликовано: 14 сентября, 2015 Жаль, по логике должно работать(если ты проверял в интерпритаторе на сайте - он кривой) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Ktlo Автор темы 789 Опубликовано: 14 сентября, 2015 (изменено) Жаль, по логике должно работать(если ты проверял в интерпритаторе на сайте - он кривой) Я проверял не на сайте, а в официальном интерпритаторе от создателей языка программирования. Версия - Lua 5.3, если что. P.S. Утренняя переписочка Изменено 14 сентября, 2015 пользователем Ktlo Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
SergOmarov 34 Опубликовано: 14 сентября, 2015 Жаль что не пашет( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Ktlo Автор темы 789 Опубликовано: 28 марта, 2016 Тема обновлена Добавлены математический метаметод __idiv() и метаметоды для битовых операторов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Zer0Galaxy 2 187 Опубликовано: 28 марта, 2016 Оператор // Это что за оператор? В Луа 5.2 не работает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Ktlo Автор темы 789 Опубликовано: 28 марта, 2016 Это что за оператор? В Луа 5.2 не работает. В Lua 5.3 его добавили. До этого такого не было. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах