Путаница с использованием "." нотация с __index и пространством имен в Lua

Я запутался в следующих двух синтаксисах, используя "."

  1. Из того, что я понимаю, __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 })
    
  2. Есть ли 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 вида таблиц:

  1. список объектов (таблицы, созданные с помощью {length=0} в вашем коде)
  2. метатабельный (содержащий __index поле), которое изменяет поведение объектов списка при попытке доступа к несуществующим полям в объекте
  3. list класс, который содержит все методы для объектов списка (например, push метод), а также служит конструктором для списка объектов
  4. метатабельный (содержащий __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) из модуля вам нужна таблица, чтобы обернуть их вместе (или использовать глобальные переменные, но это считается плохим стилем).

Другие вопросы по тегам