如何检查 Lua 中是否存在 table 子字段?
How to check if a table sub-field exist in Lua?
我正在尝试使用 Wikidata 的 Lua 模型。
我需要在维基数据的实体中搜索特定的 ID :
subjectitemofthisproperty = 'Q' .. tostring( entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] )
主要问题是某些实体没有 entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"]
子字段。
所以 Lua returns:
Lua error in Module:LoPwS_row at line 80: attempt to index field
'P1629' (a nil value).
如果我做一个:
if entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] ~= nil then
它不会工作,因为条件调用字段,然后,return同样的错误。
有没有简单的测试字段是否存在的方案?谢谢!
您可以使用 proxy-metatable 和可疑的空对象模式来解决这个问题。
空对象可以如下所示:
local Null = {}
local NullProto = { __index = function(t,k) return Null end }
setmetatable(Null, NullProto)
Null
将始终 return 当您尝试对其编制索引时。
解决方案的关键思想是为原始 table 创建一个代理对象,它将使用以下逻辑:
- 如果原始 table 中不存在某些键,则 return 空对象
- 如果原始文件中存在某些密钥 table
- 如果引用的值是原始类型,则 return 值
- 如果引用的值是 table 类型然后用代理和 return
包装它
代码可能如下所示
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'
我不建议您在生产环境中使用此代码,因为它没有经过适当的测试,但我希望它能帮助您构建一个可靠的解决方案。
有关元table的更多详细信息,请参阅https://www.lua.org/pil/13.4.html。
如果 table 链中某处有 nil
,您可以编写一个简单的函数 returns 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
我正在尝试使用 Wikidata 的 Lua 模型。
我需要在维基数据的实体中搜索特定的 ID :
subjectitemofthisproperty = 'Q' .. tostring( entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] )
主要问题是某些实体没有 entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"]
子字段。
所以 Lua returns:
Lua error in Module:LoPwS_row at line 80: attempt to index field 'P1629' (a nil value).
如果我做一个:
if entity['claims']['P1629'][1]["mainsnak"]["datavalue"]["value"]["numeric-id"] ~= nil then
它不会工作,因为条件调用字段,然后,return同样的错误。
有没有简单的测试字段是否存在的方案?谢谢!
您可以使用 proxy-metatable 和可疑的空对象模式来解决这个问题。 空对象可以如下所示:
local Null = {}
local NullProto = { __index = function(t,k) return Null end }
setmetatable(Null, NullProto)
Null
将始终 return 当您尝试对其编制索引时。
解决方案的关键思想是为原始 table 创建一个代理对象,它将使用以下逻辑:
- 如果原始 table 中不存在某些键,则 return 空对象
- 如果原始文件中存在某些密钥 table
- 如果引用的值是原始类型,则 return 值
- 如果引用的值是 table 类型然后用代理和 return 包装它
代码可能如下所示
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'
我不建议您在生产环境中使用此代码,因为它没有经过适当的测试,但我希望它能帮助您构建一个可靠的解决方案。 有关元table的更多详细信息,请参阅https://www.lua.org/pil/13.4.html。
如果 table 链中某处有 nil
,您可以编写一个简单的函数 returns 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