检查Lua表成员是否存在于任何级别

检查Lua表成员是否存在于任何级别,lua,lua-table,Lua,Lua Table,我需要检查一个成员是否存在于一个表中,该表不在下一级,而是沿着一条成员路径 foo = {} if foo.bar.joe then print(foo.bar.joe) end 这将抛出一个尝试索引字段“bar”(一个nil值),因为bar没有定义 我通常的解决办法是一件一件地测试链条 foo = {} if foo.bar and foo.bar.joe then print(foo.bar.joe) end 但是,当有许多嵌套表时,这可能非常乏味。有没有比逐件测试更好的方法?我

我需要检查一个成员是否存在于一个表中,该表不在下一级,而是沿着一条成员路径

foo = {}
if foo.bar.joe then
  print(foo.bar.joe)
end
这将抛出一个
尝试索引字段“bar”(一个nil值)
,因为bar没有定义

我通常的解决办法是一件一件地测试链条

foo = {}
if foo.bar and foo.bar.joe then
  print(foo.bar.joe)
end

但是,当有许多嵌套表时,这可能非常乏味。有没有比逐件测试更好的方法?

我不明白你所说的“沿着成员的路径”是什么意思。从这个例子中,我假设您试图在“子表”中找到一个值


一个简单的例子。如果使用这样的函数,可以传递
foo
表和
joe
字符串以查看
foo.*.joe
是否存在。希望这能有所帮助。

我想您正在寻找以下线索:

debug.setmetatable(nil, {__index = {}})

foo = {}
print(foo.bar.baz.quux)
print(({}).prd.krt.skrz.drn.zprv.zhlt.hrst.zrn)  -- sorry ))
local function get(Obj, Field, ...)
    if Obj == nil or Field == nil then
        return Obj
    else
        return get(Obj[Field], ...)
    end
end

local foo = {x = {y = 7}}
assert(get() == nil)
assert(get(foo) == foo)
assert(get(foo, "x") == foo.x)
assert(get(foo, "x", "y") == 7)
assert(get(foo, "x", "z") == nil)
assert(get(foo, "bar", "joe") == nil)
assert(get(foo, "x", "y") or 41 == 7)
assert(get(foo, "bar", "joe") or 41 == 41)
local Path = {foo, "x", "y"}
assert(get(table.unpack(Path)) == 7)
get
只需遍历给定路径,直到遇到nil。似乎能胜任这项工作。尽管如此,你还是可以想出一个比“得到”更好的名字

与往常一样,与
组合时要小心

伊戈尔的聪明回答给我留下了深刻的印象,但总的来说,我认为我们不应该依赖这些黑客

另请参见

  • Lua 5.2的“安全表导航”修补程序:

  • 关于这个问题的冗长讨论:

  • 相关技术:

  • 我怀疑MetaLua中已经实现了一些相关的功能,但目前我找不到


    • 如果我正确理解了您的问题,这里有一种可能性:

      function isField(s)
        local t
        for key in s:gmatch('[^.]+') do
          if t == nil then
            if _ENV[ key ] == nil then return false end
            t = _ENV[ key ]
          else
            if t[ key ] == nil then return false end
            t = t[ key ]
          end
          --print(key) --for DEBUGGING
        end
        return true
      end
      
      -- To test
      
      t = {}
      t.a = {}
      t.a.b = {}
      t.a.b.c = 'Found me'
      
      if isField('t.a.b.c') then print(t.a.b.c) else print 'NOT FOUND' end
      if isField('t.a.b.c.d') then print(t.a.b.c.d) else print 'NOT FOUND' end
      
      更新:根据cauterite的建议,这里有一个版本也适用于本地人,但必须有两个论点:(

      foo={}

      foo.boo={}

      foo.boo.jeo={}

      foo.boo.joe是foo['boo']['joe']等等

      我做下一个函数

      function exist(t)
      
          local words = {}
          local command
      
          for i,v in string.gmatch(t, '%w+') do words[#words+1] = i end
      
          command = string.format('a = %s', words[1])
      
          loadstring(command)()
      
          if a == nil then return false end
      
          for count=2, #words do
              a = a[words[count]]
              if a == nil then return false end
          end
      
          a = nil
          return true
      end
      
      foo = {}
      foo.boo = {}
      foo.boo.joe = {}
      
      print(exist('foo.boo.joe.b.a'))
      
      使用loadstring生成temp变量。我的lua ver是5.1


      删除5.2 5.3中的loadstring,而不是使用load来搜索表中任何级别的元素,我将使用如下方法:

      function exists(tab, element)
          local v
          for _, v in pairs(tab) do
              if v == element then
                  return true
              elseif type(v) == "table" then
                  return exists(v, element)
              end
          end
          return false
      end
      
      testTable = {{"Carrot", {"Mushroom", "Lettuce"}, "Mayonnaise"}, "Cinnamon"}
      print(exists(testTable, "Mushroom")) -- true
      print(exists(testTable, "Apple")) -- false
      print(exists(testTable, "Cinnamon")) -- true
      

      foo.bar.joe
      相当于
      \u G['foo'].bar.joe
      (如果作用域中没有局部变量
      foo
      )而不是
      \u G['foo.bar.joe']
      !您无法使用普通变量语法访问后者,因为它包含对变量名无效的字符。现在它引发了一个错误(与问题中一样,出于同样的原因)。我建议将其实现为
      isField(t,s)
      -尝试从字符串中的第一个组件获取表只适用于全局。例如,
      local u={v=1};assert(isField('u.v'))
      失败。相应地进行了更新。谢谢。我很惊讶这个简单而优雅的答案没有得到任何支持。
      function exist(t)
      
          local words = {}
          local command
      
          for i,v in string.gmatch(t, '%w+') do words[#words+1] = i end
      
          command = string.format('a = %s', words[1])
      
          loadstring(command)()
      
          if a == nil then return false end
      
          for count=2, #words do
              a = a[words[count]]
              if a == nil then return false end
          end
      
          a = nil
          return true
      end
      
      foo = {}
      foo.boo = {}
      foo.boo.joe = {}
      
      print(exist('foo.boo.joe.b.a'))
      
      function exists(tab, element)
          local v
          for _, v in pairs(tab) do
              if v == element then
                  return true
              elseif type(v) == "table" then
                  return exists(v, element)
              end
          end
          return false
      end
      
      testTable = {{"Carrot", {"Mushroom", "Lettuce"}, "Mayonnaise"}, "Cinnamon"}
      print(exists(testTable, "Mushroom")) -- true
      print(exists(testTable, "Apple")) -- false
      print(exists(testTable, "Cinnamon")) -- true