Как проверить, существует ли подполе таблицы в Lua? - PullRequest
0 голосов
/ 03 ноября 2018

Я пытаюсь использовать модели Lua Wikidata.

Мне нужно найти конкретный идентификатор в сущностях Викиданных:

    subjectitemofthisproperty = 'Q' .. tostring( entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] )

Основная проблема в том, что некоторые сущности не имеют подполя entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"].

Так что Луа возвращается:

Ошибка Lua в модуле: LoPwS_row в строке 80: попытка индексировать поле «P1629» (нулевое значение).

Если я сделаю:

if entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] ~= nil then

Это не будет работать, потому что условие вызывает поле, а затем возвращает ту же ошибку.

Есть ли простое решение для проверки существования поля? Спасибо!

Ответы [ 2 ]

0 голосов
/ 03 ноября 2018

Вы можете написать простую функцию, которая возвращает nil, если где-то в цепочке таблиц есть nil. Давайте назовем это lookup:

function lookup(t, ...)
    for _, k in ipairs{...} do
        t = t[k]
        if not t then
            return nil
        end
    end
    return t
end

-- Test it
t = {a = {b = {c = 5}}}
lookup(t, 'a', 'x', 'b') -- Returns nil
lookup(t, 'a', 'b', 'c') -- Returns 5
0 голосов
/ 03 ноября 2018

Вы можете решить эту проблему, используя прокси-метатабельный и сомнительный шаблон Null Object. Нулевой объект может выглядеть так:

local Null = {}
local NullProto = { __index = function(t,k) return Null end }
setmetatable(Null, NullProto)

Null всегда будет возвращаться при попытке индексирования.

Ключевой идеей решения является создание прокси-объекта для исходной таблицы, который будет использовать следующую логику:

  • Если какой-либо ключ не существует в исходной таблице, вернуть Null Object
  • Если какой-то ключ существует в исходной таблице
    • Если ссылочное значение имеет примитивный тип, вернуть значение
    • Если указанное значение относится к табличному типу, обернуть его прокси и вернуть

Код может выглядеть следующим образом

 function make_safe_table(nonsafe)
    local proto = {
    __index = function(t, k)
        local val = nonsafe[k]
        if val == nil then
            return Null
        elseif type(val) == 'table' then
            return make_safe_table(val)
        else
            return val
        end
    end
    }
    return setmetatable({}, proto)
end

Вы можете использовать эту функцию следующим образом:

local original = {
    nested = {
        deep = { hidden = 'value'}
    },
    simple = 'simple',
    [3] = 'third'
}
local safe_original = make_safe_table(original)
print(safe_original.not_exists == Null) -- true
print(safe_original.nested.not_exists == Null) -- true
print(safe_original.nested.deep.not_exists == Null) -- true
print(safe_original.not_exists.still_not_exists == Null) -- true
print(safe_original.nested.deep.hidden) -- 'value'
print(safe_original.simple) -- 'simple'
print(safe_original[3]) -- 'third'

Я бы не рекомендовал вам использовать этот код в производственной среде, поскольку он не был должным образом протестирован, но я надеюсь, что он поможет вам создать надежное решение. См. https://www.lua.org/pil/13.4.html для более подробной информации о метатаблицах.

...