如何在 Lua 中检查表子字段是否存在?

我正在尝试使用维基数据的 Lua 模型。

我需要在维基数据的实体中搜索特定的 ID:

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

主要问题在于某些实体没有 entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] 子字段。

因此 Lua 返回:

Module: LoPwS_row 中的 Lua 错误,第 80 行: 尝试索引字段 'P1629'(一个 nil 值)。

如果我执行以下操作:

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

它不起作用,因为该条件调用字段,然后返回相同的错误。

是否有一种简单的方法来测试字段是否存在?谢谢!

点赞
用户10593333
用户10593333

你可以使用代理元表和可疑的 Null 对象模式来解决这个问题。 Null 对象可以像这样:

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

当你尝试索引 Null 对象时,它将始终返回自身。

解决方案的关键思想是为原始表创建代理对象,该代理对象将使用以下逻辑:

  • 如果某个键不存在于原始表中,则返回 Null 对象。

  • 如果某个键存在于原始表中,则

    • 如果引用值是原始类型,则返回该值。
    • 如果引用值是表类型,则使用代理进行包装并返回。

代码可能如下所示:

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

2018-11-03 20:17:57
用户1492351
用户1492351

你可以编写一个简单的函数,如果表链中有 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

-- 测试一下
t = {a = {b = {c = 5}}}
lookup(t, 'a', 'x', 'b') -- 返回 nil
lookup(t, 'a', 'b', 'c') -- 返回 5
2018-11-03 20:35:12