假设我有一张巨大的桌子,例如:
test.test[1].testing.test.test_test
不保证该表存在。包含它的表也不是。我希望能够做到:
if test.test[1].testing.test.test_test then
print("it exits!")
end
但是,当然,如果尚未定义任何索引,这会给我一个“尝试索引?(零值)”错误。很多次,我最终都会做这样的事情:
if test then
if test.test then
if test.test[1] then
if test.test[1].testing then -- and so on
有没有更好、更省事的方法来实现这一点?
您可以编写一个函数,该函数需要查找键列表,并在找到该条目时执行您想要的任何操作。这是一个例子:
function forindices(f, table, indices)
local entry = table
for _,idx in ipairs(indices) do
if type(entry) == 'table' and entry[idx] then
entry = entry[idx]
else
entry = nil
break
end
end
if entry then
f()
end
end
test = {test = {{testing = {test = {test_test = 5}}}}}
-- prints "it exists"
forindices(function () print("it exists") end,
test,
{"test", 1, "testing", "test", "test_test"})
-- doesn't print
forindices(function () print("it exists") end,
test,
{"test", 1, "nope", "test", "test_test"})
顺便说一句,解决此类问题的函数式编程概念是“Maybe monad”。你也许可以用 Lua 的 monad 实现 来解决这个问题,尽管这不是很好,因为没有语法糖。
debug.setmetatable(nil, { __index=function () end })
print(test.test[1].testing.test.test_test)
test = {test = {{testing = {test = {test_test = 5}}}}}
print(test.test[1].testing.test.test_test)
您也可以使用空表:
debug.setmetatable(nil, { __index={} })
if
test and
test.test and
test.test[1] and
test.test[1].testing and
test.test[1].testing.test and
test.test[1].testing.test.test_test
then
print("it exits!")
end
但是,当然,我会尝试重构它,以便不需要那么多嵌套。
if (((((test or {}).test or {})[1] or {}).testing or {}).test or {}).test_test then print"it exists!" end
只需将每个
t[k]
或
t.k
替换为 (t or {})[k]
或 (t or {}).k
,这将导致 nil
被替换为 {}
,以便后续索引操作成功(但可能再次产生 nil
)。 警告:这很可能会创建不必要的垃圾表;你的 Lua 实现不太可能“优化它们”。它也比自己写一个助手方便。我通常编写的帮助程序使用可变参数来避免创建临时表:
function nilget(t, ...)
for i = 1, select("#", ...) do
if t == nil then return nil end
t = t[select(i, ...)]
end
return t
end
用法:
nilget(test, "test", 1, "testing", "test", "test_test")
。
我建议不更改nil
的调试元表;这有效地将所有索引操作放宽为零合并索引操作,这很可能隐藏错误。