代码之家  ›  专栏  ›  技术社区  ›  GenerationTech

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

  •  0
  • GenerationTech  · 技术社区  · 7 年前

    我需要抓取一组大小不确定的数据,并为其构建一个表键/值索引。由于我事先不知道维数,似乎我必须使用递归函数。我的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脚本接口文档很少。它运行的是Lua5.1,API没有很好的文档记录,也没有经常更新。据我所知,所有内容都存储在G中,所以我想写一些东西来对API进行反向工程。我有一个可运行的递归函数(此处未显示),它枚举所有的返回值。对于该返回值,它只构建一个带注释的字符串,并逐步构建在该字符串上。这一切都很好,非常有用,但它显示了更多的API接口;所有实际的数据元素都包含在内,因此我必须筛选30000条记录,以确定一个包含大约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)

    我有另一个递归显示表的函数,这样我可以查看我的结果,看到我只得到一个返回值,它只包含顶级的值(我排除了内置的Lua函数/值,因为我只对应用程序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 ",
    }
    
    3 回复  |  直到 7 年前
        1
  •  0
  •   Paul Kulchenko    7 年前

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

    local table_api = {}
    

    (请注意,您正在通过 table 与全局变量冲突的变量 桌子 变量,当前未在函数中使用。)

        2
  •  0
  •   BotOfWar    7 年前

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

    另一个递归显示表的函数

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

    我得到了您的初始代码和当前代码(tableAPIShow)来处理我的simple table serialize function :在tableAPIShow()中实现时,将完整导出整个\u全局表并对其进行格式化。

    测试代码:

    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) 但事实并非如此。

        3
  •  0
  •   user2262111    7 年前

    正如保罗·库尔琴科所说,你需要学会使用当地人( https://www.lua.org/pil/4.2.html ). 全局变量post存在,直到加载新的lua\U状态(新的环境可能是新的进程,具体取决于您使用的解释器)。因此,有一个技巧是,对于不想离开函数或编译单元的任何内容,始终使用局部变量。

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

    我认为您正在尝试对数据表进行序列化。然而,这并不是真正必要的。您可以对给定的表进行卷影复制或深度复制。卷影复制是指不深入研究键中的表的深度等。深度复制是指将表的键中的表复制到表的键中。。。等

    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。卢阿
    https://gist.github.com/Deco/3985043

    还可以使用句点索引表:

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

    要序列化整个表,只需过滤掉不需要的垃圾并递归遇到的每个表即可。请注意\u G、package和\u 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)