如何在Lua中使用递归函数来动态构建表而不会在每次调用时覆盖它

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Lua中使用递归函数来动态构建表而不会在每次调用时覆盖它相关的知识,希望对你有一定的参考价值。

我需要抓取一组不确定大小的数据并构建它的表键/值索引。由于我事先不知道尺寸,似乎我必须使用递归函数。我的Lua技能非常新颖和肤浅。我很难理解如何处理从函数调用返回表。

请注意,这是一个Lua 5.1脚本处理器

API = function(tbl)
  local table_api = {}
  -- do stuff here with target data and add to table_api
  table_api["key"] = value
  -- then later there is a need to recurse deeper into target data
  table_api["lower"] = API(var)
  return table_api
end

result = API(source_data)

在大多数其他语言中,我知道有一些方法可以使递送行table_api["lower"] = API(var)工作,但由于Lua通过引用执行表变量,我的返回子表只是一直被覆盖,我的结果是它的最后一点应该是什么。

仅仅是为了我的目的背景:我正在使用的商业应用程序有一个弱记录的Lua脚本界面。它正在运行Lua 5.1,并且API没有很好地记录并经常更新。据我所知,一切都存储在_G中,所以我想写一些东西来反向工程API。我有一个有效的递归函数(这里没有显示)枚举_G的所有内容。对于该返回值,它只是构建一个带注释的字符串并逐步构建在字符串上。这一切都很好,非常有用,但它显示了更多的API接口;包含所有实际数据元素,因此我必须筛选30,000条记录以确定大约500个条款的API集。为了确定API,我试图在这个问题中讨论使用这个子表返回值递归函数。我在这里展示的代码只是较大函数的一个小的蒸馏子集。

我将继续并在此处包含完整代码。我希望逐步构建每个API级别的大型表,任何子级别,最后在最低级别使用的任何键。

最后,我期待有一张表,我可以这样解决:

result["api"]["label"]["api"]["sublabel"]["value"]["valuename"]

完整代码:

tableAPIShow = function(tbl, table_track)
  table_track = table_track or {}
  local table_api = {}

  if type(tbl) == 'table' then
    -- Check if values are tables.
    local parent_table_flag = true
    for ind,val in pairs(tbl) do
      if type(val) ~= 'table' then
        parent_table_flag = false
        break
      end
    end

    -- If all children are table type, check each of them for subordinate commonality
    local api_flag = false
    if parent_table_flag == true then
      local child_table = {}
      local child_table_flag = false
      api_flag = true
      for ind,val in pairs(tbl) do
        -- For each child table, store the names of the indexes.
        for sub_ind,sub_val in pairs(val) do
          if child_table_flag == false then -- First time though, create starting template view of typical child table.
            child_table[sub_ind] = true -- Store the indexes as a template table.
          elseif child_table[sub_ind] == nil then -- Otherwise, test this child table compared to the reference template.
            api_flag = false
            break
          end
        end
        if api_flag == false then -- need to break out of nested loop
          break
        end
        child_table_flag = true
      end
    end

    if api_flag == true then
      -- If everything gets to here, then this level is an API with matching child tables below.
      for ind,val in pairs(tbl) do
        if table_api["api"] == nil then
          table_api["api"] = {}
        end
        table_api["api"][ind] = tableAPIShow(val, table_track)
      end
    else
      -- This level is not an API level, determine how to process otherwise.
      for ind,val in pairs(tbl) do
        if type(val) == 'table' then
          if table_track[val] ~= nil then -- Have we already recursed this table?
          else
            table_track[val] = true
            if table_api["table"] == nil then
              table_api["table"] = {}
            end
            table_api["table"][ind] = tableAPIShow(val, table_track)
          end
        else -- The children are not tables, they are values
          if table_api["value"] == nil then
            table_api["value"] = {}
          end
          table_api["value"][ind] = val
        end
      end
    end
  else
    -- It's not a table, just return it.
    -- Probably never use this portion because it's caught on upper level recurse and not called
    return tbl
  end
  return table_api
end

我在主脚本中调用这个函数是这样的:

local str = tableAPIShow(_G)

我有另一个函数,递归显示一个表,所以我可以查看我的结果,看到我只得到一个返回值,只包含_G的顶级值(我已经排除了内置的Lua函数/值)因为我只对Application API感兴趣:

{
[value] = table: 00000000F22CB700 {
    [value][_VERSION] = Application/5.8.1 (x86_64; Windows NT 10.0.16299),
    [value][tableAPIShow] = "function: 00000000F22C6DE0, defined in (121-231) C:\Users\user\AppData\Local\Temp\APP\/~mis00002690 ",
    [value][_FINAL_VERSION] = true,
    [value][Path] = ./Scripts/Database/elements/,
    [value][class] = "function: 00000000F1953C40, defined in (68-81) Scripts/Common/Class.lua ",
    [value][db_path] = ./Scripts/Database/,
    [value][merge_all_units] = "function: 00000000F20D20C8, defined in (2242-2250) Scripts/Database/db_merge.lua ",
}
答案

您只需要本地化存储表的变量,它将按预期工作:

local table_api = {}

(请注意,您传递的table变量与全局table变量冲突,并且当前未在函数中使用。)

另一答案

我倾向于相信你的tableAPIShow函数代码正常工作和

另一个递归显示表的函数

无法完全序列化您的表。因此,您没有看到tableAPIShow()返回的表的更深层次。

我得到了你的初始和当前代码(tableAPIShow)以使用我的简单table serialize function:整个_Global表完全导出并格式化,因为你在tableAPIShow()中实现它。

测试代码:

apiMassiveTable = {
    api = {
        get = {
            profile = {"profileID", "format"},
            name = {"profileID", "encoding"},
            number = {"profileID", "binary"}
        },
        set = {
            name = {"apikey", "profileID", "encoding", "newname"},
            number = {"apikey", "profileID", "binary", "newnumber"}
        },
        retrieve = {}
    },
    metadata = {version="1.4.2", build="nightly"}
}

-- tableAPIShow implemenation here

table.serialize = dofile("serialize.lua")
print(table.serialize("myNameForAHugeTable", tableAPIShow(_G)))

PS:无论你使用什么序列化函数,它都应该引用像Application/5.8.1 (x86_64; Windows NT 10.0.16299)那样的字符串。

另一答案

就像@Paul Kulchenko所说,你需要学习使用当地人(https://www.lua.org/pil/4.2.html)。全局变量后存在,直到加载新的lua_State(新环境,可能是一个新进程,具体取决于您使用的解释器)。因此,一个提示是始终使用局部变量来处理您不想离开函数或离开编译单元的任何内容。

想象字典这样的表:一个单词附加到一个定义。因此,定义就是数据。

我认为你要做的是序列化数据表。但是,这不是必要的。您可以对给定表进行卷影复制或深层复制。阴影副本是指您没有深入研究密钥等中找到的表格的深度。深层复制是指在表格的键中复制表格中的表格...等等。

local shallow_copy = function(tab)
    local rep_tab = {}

    for index, value in pairs(tab)do
        rep_tab[index] = value
    end

    return rep_tab
end

-- because local variable is not defined or declared immediately on a 1-liner,
--   a declaration has to exist so that deep_copy can be used
--   lets metatable execute functions
local deep_copy
deep_copy = function(tab)
    local rep_tab = {}

    for index, value in pairs(tab)do
        if(type(value) == "table")then
            rep_tab[index] = deep_copy(value)
        else
            rep_tab[index] = value
        end
    end

    return rep_tab
end

Deco的deepcopy.lua https://gist.github.com/Deco/3985043

您还可以使用句点索引表:

local tab = {}
tab.abc = 123
tab["def"] = 456
print(tab.abc, tab["def"])

要序列化整个_G,您只需过滤掉不需要的垃圾并递归遇到的每个表。注意_G,包和_ENV,因为如果定义它将返回到开始。

-- use cap as a whitelist
enumerate_dir = function(dir, cap)
    local base = {}

    for i, v in pairs(dir) do
        -- skip trouble
        if(i ~= "_G" and i ~= "_ENV" and i ~= "package")then
            if(type(v) == "table")then -- if we have a table
                base[i] = enumerate_dir(v, cap)
            else
                for k, n in pairs(cap) do
                    if(type(v) == n)then -- if whitelisted
                        base[i] = tostring(v)
                    end
                end
            end
        end
    end

    return base
end

-- only functions and tables from _G please
local enumeration = enumerate_dir(_G, {"function", "table"})

-- do some cool quips to get a basic tree system
prefix = ""
recursive_print = function(dir)
    for i, v in pairs(dir)do
        if(type(v) == "table")then
            print(prefix, i, v)
            prefix = prefix .. ">"
            recursive_print(v)
        else
            print(prefix, i, v)
        end
    end
    if(#prefix > 0)then
        prefix = prefix:sub(1, #prefix-1)
    end
end

recursive_print(test)

以上是关于如何在Lua中使用递归函数来动态构建表而不会在每次调用时覆盖它的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 lua 构建中缀 TO 前缀函数

Lua - 表不会从函数中插入

如何仅针对表而不针对触发器和函数执行 pg_dump?

redis使用lua

如何使用递归函数更新表?

如何仅使用堆栈来实现递归函数?