Путаница с использованием "." нотация с __index и пространством имен в Lua
Я запутался в следующих двух синтаксисах, используя "."
Из того, что я понимаю,
__index
вызывается, когда ключ не существует в таблице, но существует в ее метатабельной таблице. Так почему же вызывается таблица списка__index
а затем назначить себяlist.__index
?list = {} list.__index = list setmetatable(list, { __call = function(_, ...) local t = setmetatable({length = 0}, list) for _, v in ipairs{...} do t:push(v) end return t end }) function list:push(t) if self.last then self.last._next = t t._prev = self.last self.last = t else self.first = t self.last = t end self.length = self.length + 1 end . . . local l = list({ 2 }, {3}, {4}, { 5 })
Есть ли
Window.mt
просто создать таблицу? Зачем намWindow = {}
в качестве пространства имен здесь?Window = {} -- create a namespace Window.mt = {} -- create a metatable Window.prototype = {x=0, y=0, width=100, height=100, } function Window.new (o) setmetatable(o, Window.mt) return o end Window.mt.__index = function (table, key) return Window.prototype[key] end w = Window.new{x=10, y=20} print(w.width) --> 100
1 ответ
Итак, почему таблица списка вызывает __index, а затем присваивает себя списку.__index?
Нигде в вашем коде не вызывается таблица списка __index
, Тем не менее, часть задания - это обычная идиома Lua (она же хак) для экономии памяти. Концептуально существует 4 вида таблиц:
- список объектов (таблицы, созданные с помощью
{length=0}
в вашем коде) - метатабельный (содержащий
__index
поле), которое изменяет поведение объектов списка при попытке доступа к несуществующим полям в объекте -
list
класс, который содержит все методы для объектов списка (например,push
метод), а также служит конструктором для списка объектов метатабельный (содержащий
__call
поле) дляlist
класс, так что вы можете позвонитьlist
таблица, как если бы это была функция
Поскольку метатабельные поля всегда начинаются с двух подчеркиваний (__
), а обычные методы обычно этого не делают, вы можете поместить метатируемые поля и обычные методы бок о бок в одну таблицу без конфликта. И это то, что здесь произошло. list
Таблица классов также служит метатабельной для списка объектов. Таким образом, используя этот прием, вы можете сэкономить память, которая обычно требуется для отдельного метатаба (размер в байтах для Lua 5.2 на Linux x86-64 показан в квадратных скобках в строках заголовка таблицы, кстати):
Window.mt просто создает таблицу?
Нет, {}
создает таблицу. Тем не менее, эта новая таблица сохраняется под ключом "mt"
в Window
таблица, вероятно, чтобы дать пользователям этого Window
"класс" прямой доступ к метатаблице, которая используется для объектов окна. Учитывая только код, который вы показали, это не является строго необходимым, и вы могли бы вместо этого использовать локальную переменную.
Зачем нам здесь Window = {} в качестве пространства имен?
В принципе, вы могли бы хранить Window.mt
, Window.new
, а также Window.prototype
отдельно, но это будет обременительно, если у вас есть несколько "классов", таких как Window
, Таким образом, вы можете избежать столкновений имен и использовать Window
"класс" выглядит лучше.
Другая причина может быть в том, что require
может возвращать только одно значение из определения модуля, и если вы хотите экспортировать несколько значений (например, new
, mt
, а также prototype
) из модуля вам нужна таблица, чтобы обернуть их вместе (или использовать глобальные переменные, но это считается плохим стилем).