1

在 Lua 4 中,许多表都有一个“n”属性,用于跟踪表内的项目数。

所有表都有这个属性吗?它可以被覆盖吗?

我问,因为我正在尝试开发一个例程,以有效的 Lua 语法递归地打印表的所有元素,并且想知道从结果中过滤所有“n”项是否安全?

谢谢。

[编辑]

这是脚本:

-- ThoughtDump v1.4.0
-- Updated: 2017/07/25
-- *****************
-- Created by Thought (http://hw2.tproc.org)
-- Updated by Mikali

-- DESCRIPTION
-- ***********
-- Parses the globals table and __TDPrints its contents to "HW2.log".
-- Can also be used to parse (i.e., pretty-print) generic tables in some cases.

-- Note: functions & variables must actually be declared in order to be parsed. 
-- Otherwise, they are ignored.
-- Note: if parsing a table other than the globals table, the __TDPrinted table
-- values may be in a different order than was originally written. Values with 
-- numerical indices are moved to the "top" of the table, followed by values 
-- with string indices, followed by tables. Functions appear in different 
-- locations, depending on whether they are indexed using a number or a string.
-- Note: despite the fact that nil values cannot be stored in tables, they are 
-- still handled.
-- Note: even though functions may be referenced within tables, a function will 
-- only be parsed correctly if it is indexed using a string that is the same as
-- the name of the function.

__TDOutputString = ""

function __TDParse(name, value, level, verbose, numbers, collapse)
    if ((name == "__TDParse") or (name == "__TDSortHash") or (name == "__TDPrint") or (name == "__TDPrintGlobals()") or (name == "__TDOutputString")) then
        return
    end
    local Element = nil
    local ValType = type(value)
    local NamType = type(name)
    local PreLevel = ""
    if (collapse == 0) then
        for i = 1, level do
            PreLevel = PreLevel .. "\t"
        end
    end
    local ComLevel = ""
    if (level ~= 0) then
        ComLevel = ","
    end
    if ((ValType == "function") or (ValType == "userdata")) then
        if (NamType == "string") then
            Element = PreLevel .. name .. " = " .. name .. ComLevel
        elseif (numbers == 1) then
            Element = PreLevel .. "[" .. name .. "] = " .. name .. ComLevel
        else
            Element = PreLevel .. name .. ComLevel
        end
    elseif (ValType == "string") then
        if (NamType == "string") then
            Element = PreLevel .. name .. " = \"" .. value .. "\"" .. ComLevel
        elseif (numbers == 1) then
            Element = PreLevel .. "[" .. name .. "] = \"" .. value .. "\"" .. ComLevel
        else
            Element = PreLevel .. "\"" .. value .. "\"" .. ComLevel
        end
    elseif (ValType == "number") then
        if (NamType == "string") then
            Element = PreLevel .. name .. " = " .. value .. ComLevel
        elseif (numbers == 1) then
            Element = PreLevel .. "[" .. name .. "] = " .. value .. ComLevel
        else
            Element = PreLevel .. value .. ComLevel
        end
    elseif (ValType == "table") then
        if (NamType == "string") then
            Element = PreLevel .. name .. " ="
        elseif (numbers == 1) then
            Element = PreLevel .. "[" .. name .. "] ="
        else
            Element = ""
        end
    elseif (ValType == "nil") then
        if (NamType == "string") then
            Element = PreLevel .. name .. " = nil" .. ComLevel
        elseif (numbers == 1) then
            Element = PreLevel .. "[" .. name .. "] = nil" .. ComLevel
        else
            Element = PreLevel .. "nil" .. ComLevel
        end
    else
        Element = PreLevel .. "-- unknown object type " .. ValType .. " for object " .. name
    end
    if (verbose == 1) then
        Element = Element .. "  -- " .. ValType .. ", tag: " .. tag(value)
    end
    if (((ValType == "table") and (NamType == "number") and (numbers == 0)) or (collapse == 1)) then
        __TDPrint(Element, 0)
    else
        __TDPrint(Element, 1)
    end
    if (ValType == "table") then
        __TDPrint(PreLevel .. "{", collapse == 0)
        __TDSortHash(__TDParse, value, level + 1, verbose, numbers, collapse)
        __TDPrint(PreLevel .. "}" .. ComLevel, 1)
    end
end

function __TDSortHash(func, tabl, level, verbose, numbers, collapse)
    local typesarray = {}
    local typescount = {}
    local keycount = 1
    local keyarray = {}
    for i, iCount in tabl do
        local thistype = type(iCount)
        if not (typesarray[thistype]) then
            typescount[thistype] = 0
            typesarray[thistype] = {}
        end
        typescount[thistype] = typescount[thistype] + 1
        typesarray[thistype][typescount[thistype]] = i
    end
    sort(typesarray)
    for i, iCount in typesarray do
        sort(iCount)
        for j, jCount in iCount do
            keyarray[keycount] = tostring(jCount)
            keycount = keycount + 1
        end
    end
    for i, iCount in keyarray do
        local tempcount = tonumber(iCount)
        if (tempcount) then
            iCount = tempcount
        end
        func(iCount, tabl[iCount], level, verbose, numbers, collapse)
    end
end

function __TDPrint(instring, newline)
    __TDOutputString = __TDOutputString .. instring
    if (newline == 1) then
        __TDOutputString = __TDOutputString .. "\n"
    end
end

function __TDPrintGlobals()
    __TDOutputString = ""
    __TDPrint("globals =", 1)
    __TDPrint("{", 1)
    __TDSortHash(__TDParse, globals(), 1, 0, 0, 0)
    __TDPrint("}\n", 1)
    local WriteFile = "$test_globals_write.lua"
    writeto(WriteFile)
    write(__TDOutputString)
    writeto()
end

__TDPrintGlobals()
4

2 回答 2

1

并非所有表都具有此属性。它可以被覆盖。

为什么不使用 for 循环遍历表?或者如果可能的话,使用 Lua 5.3 ;)

在 Lua 中,这被称为 table for 循环,在现代 Lua 中,它被称为通用 for 循环。

table for 语句遍历给定表的所有对(索引,值)。它具有以下语法:

stat ::= for name `,' name in exp1 do block end

像这样的 for 语句

for index, value in exp do block end

相当于代码:

do
  local _t = exp
  local index, value = next(t, nil)
  while index do
      block
      index, value = next(t, index)
     end
  end

请注意以下事项:

  • _t 是一个不可见的变量。该名称仅用于说明目的。

  • 如果您分配给块内的索引,则行为未定义。

  • 如果您在遍历期间更改表 _t,则行为未定义。

  • 变量 index 和 value 是语句的局部变量;你不能在 for 结束后使用它们的值。

  • 您可以使用 break 来退出 for。如果需要 index 或 value 的值,在 break 之前将它们分配给其他变量。

  • 遍历表元素的顺序是未定义的,即使对于数字索引也是如此。如果要按数字顺序遍历索引,请使用数字 for。

参考Lua手册4.4.4

https://www.lua.org/manual/4.0/manual.html#4.4

于 2017-07-25T19:43:55.900 回答
1

在 Lua 4.x 中的表格中,n 只是表格的一个元素,就像表格可以包含的任何其他元素一样,但它不是表格机制本身的一部分。因此,它可以被覆盖或删除。

一些函数使用它,比如 tinsert() 和其他表函数:

local tbl = { n=0 }
tinsert(tbl, 123)
print(tbl.n)      --> 1

这非常有用,因为 getn() 函数仅给出表的最高编号索引。但是如果表中只有命名元素或混合或数字索引和命名索引,则 getn() 不会反映表中元素的实际数量。如果始终使用 tinsert() 等表函数插入(或删除)元素,则 n 是表中元素的准确数量。

Lua 4.x --> Lua 5.x equivalent:
getn(tbl)       #tbl
tinsert(tbl,e)  table.insert(tbl,e)  or   tbl:insert(e)

当然,您仍然可以使用简单的表格访问在表格中添加元素。但由于 n 可能非常有用,因此请尽量保持更新。

tbl["Bla"] = 234   
tbl.Bli = 345
tbl.n = tbl.n + 2

如果 n 不存在于表中,但某处的代码需要它,则可以使用 for 循环添加它:

local tbl = {1,2,3,4,5,6}; tbl.a=11; tbl.b=22; tbl.c=33
local n = 0
for ie, e in tbl do
   n = n + 1
end
tbl.n = n

或 foreach 循环:

local tbl = {1,2,3,4,5,6}; tbl.a=11; tbl.b=22; tbl.c=33
tbl.n = 0
foreach(tbl, function() %tbl.n = %tbl.n + 1 end )

注1:tbl.n初始化为0会给出表中元素的个数,包括n。这里 tbl.n 的结果是 10。由于最终我们不希望将 n 计为表的真实元素(即是),而只计算其他元素,因此我们应该将 n 初始化为 -1。

注意 2:这里使用 Lua 4.x 上值运算符 % 是因为 tbl 变量在 foreach 循环的函数(不在范围内)中不可访问。可以使用 %tbl 到达。但是,upvalue 始终是只读的,因此不能更改 tbl 变量。以下将在函数中产生错误:

%tbl = { }  -- change the reference to another table
%tbl = 135  -- change the ref to the table for a number (or a string, ...)

由于 tbl 变量实际上包含对表的引用,因此引用的表可以修改,因此元素 n 可以毫无问题地更改(以及表的其他元素)。

%tbl.n = %tbl.n + 1   -- increment the element n of the referenced table

注意 3:可以使用全局变量 tbl,但最好始终使用局部变量。访问局部变量也比全局更快。

于 2019-04-19T21:52:08.730 回答