Доступ к глубоко вложенной таблице без ошибок?

Для поля внутри глубоко вложенной таблицы, например text.title.1.font. Даже если вы используете

if text.title.1.font then ... end

это приведет к ошибке типа «попытка проиндексировать глобальный« текст »(нулевое значение)», если какой-либо уровень таблицы на самом деле не существует. Конечно, можно попробовать проверить наличие каждого уровня таблицы, но это кажется довольно громоздким. Мне интересно, есть ли более безопасный и красивый способ справиться с этим, чтобы при ссылке на такой объект значение nil было бы вместо того, чтобы вызывать ошибку?


person Dionysian    schedule 21.05.2014    source источник
comment
Autotables / Autovivification (см. 1 или 2) в большинстве случаев также работают достаточно хорошо, не изменяя метатаблицу nil и не требуя библиотеки отладки. Кстати, 1 недопустим для синтаксиса . или :. Индекс, индексированный таким образом, должен начинаться с буквы (a-Z) или символа подчеркивания. (3)   -  person dualed    schedule 21.05.2014


Ответы (2)


Способ сделать это, не вызывающий множества ошибок, - это явно указать Lua, какие поля каких таблиц должны быть таблицами по умолчанию. Вы можете сделать это с помощью метатаблиц. Ниже приведен пример, но он действительно должен быть настроен в соответствии с тем, как вы хотите, чтобы ваши таблицы были структурированы.

-- This metatable is intended to catch bugs by keeping default tables empty.
local default_mt = {
  __newindex =
    function()
      error(
    'This is a default table. You have to make nested tables the old-fashioned way.')
    end
}

local number_mt = {
  __index =
    function(self, key)
      if type(key) == 'number' then
    return setmetatable({}, default_mt)
      end
    end
}

local default_number_mt = {
  __index = number_mt.__index,
  __newindex = default_mt.__newindex
}

local title_mt = {__index = {title = setmetatable({}, default_number_mt)}}

local text = setmetatable({}, title_mt)

print(text.title[1].font)
person luther    schedule 21.05.2014

Предложение Егора debug.setmetatable(nil, {__index = function()end}) применить проще всего. Имейте в виду, что он не имеет лексической области видимости, поэтому, когда он включен, он будет «включен» до тех пор, пока не будет выключен, что может иметь непредвиденные последствия в других частях вашего кода. См. эту ветку для обсуждения и некоторых альтернатив.

Также обратите внимание, что text.title.1.font, вероятно, должно быть text.title[1].font или text.title['1'].font (и эти два не совпадают).

Еще одна, более подробная, но все же полезная альтернатива:

if (((text or {}).title or {})[1] or {}).font then ... end
person Paul Kulchenko    schedule 21.05.2014