Lua:检查表是否可以通过从0开始的ipairs循环遍历

我有一个很好的 Lua 表解析器,能打印出漂亮的 Lua 代码,而且我很喜欢它......它运行得很好。但是有一个小问题......如果我要打印具有任何整数键的表或数组,它将使用 pairs(具有讽刺意味的是,它不会破坏代码),但我宁愿它使用 ipairs。所以,我想知道,有没有可能检查一张表(不用看它)是否可以首先使用 ipairs 循环遍历它,否则使用 pairs。然后,有没有一种方法可以从 0 开始循环,而不是 Lua 的默认值 1?

Lua 表解析器(在谷歌上找到的基础代码,我改变了它使它更适合打印数组)...

function TableParser(name, object, tabs)
    local function serializeKeyForTable(k)
        if type(k)=="number" then
            return ""
        end
        if string.find(k,"[^A-z_-]") then
            return k
        end
        return k
    end
    local function serializeKey(k)
        if type(k)=="number" then
            if k == 0 then
                return "\t[" .. k .."] = "
            else
                return "\t"
            end
        end
        if string.find(k,"[^A-z_-]") then
            return "\t" .. k .. " = "
        end
        return "\t" .. k .. " = "
    end
    if not tabs then tabs = "" end
    local function serialize(name, object, tabs) -- = {
        local output = tabs .. (name ~= "" and name .. " = " or "") .. "{" .. "\n"
        for k,v in pairs(object) do
            if type(v) == "number" then
                output = output .. tabs .. serializeKey(k) .. v
            elseif type(v) == "string" then
                output = output .. tabs .. serializeKey(k) .. string.format("%q",v)
            elseif type(v) == "table" then
                output = output .. serialize(serializeKeyForTable(k), v, tabs.."\t")
            elseif type(v) == "boolean" then
                output = output .. tabs .. serializeKey(k) .. tostring(v)
            else
                output = output .. tabs .. serializeKey(k) .. "\"" .. tostring(v) .. "\""
            end
            if next(object,k) then
                output = output .. ",\n"
            end
        end
        return output .. "\n" .. tabs .. "}"
    end
    return serialize(name, object, tabs)
end
点赞
用户3677376
用户3677376

所以我想知道是否可以检查一个表格(不用实际去查看它),如果可以使用ipairs对其进行循环,否则使用pairs

不要检查,直接使用!首先使用ipairs并跟踪ipairs迭代器返回的最大键。然后再次使用pairs进行迭代,并忽略1和从ipairs中获取的最大键之间的所有整数键。

如果你真的想要检查ipairs是否会做任何事情,那么请查看表格中的索引1(rawget(object,1)~=nil)。在不迭代表格的情况下,不可能检查ipairs是否将覆盖表格中的所有元素。

接下来可能会有问题:有没有办法从0开始循环,而不是默认的Lua 1?

ipairs(t)返回三个值:一个迭代器函数,表格t作为状态变量和初始索引值0。如果你将-1作为初始索引值,则ipairs将从0开始迭代(迭代器函数总是在使用索引值之前递增一):

t = { 1, 2, 3, [0] = 0 }
for i,v in ipairs( t ), t, -1 do  -- 仅使用ipairs返回的第一个值
  print( i, v )
end

然而,请注意,Lua 5.2已经添加了对一个新的元方法__ipairs的支持,它允许你返回一个自定义的迭代器三元组来用于ipairs迭代,并且在这种情况下返回的迭代器函数可能需要不同的状态和初始索引值。

编辑: 要将建议结合到你的代码中,请在for k,v in pairs(object) do循环之前插入:

local largest = 0
for k,v in ipairs(object) do
    largest = k
    local t = type(v)
    if t == "table" then
        output = output .. tabs .. "\t" .. serialize( "", v, tabs.."\t" )
    elseif t == "string" then
        output = output .. tabs .. "\t" .. string.format("%q", v)
    else
        output = output .. tabs .. "\t" .. tostring(v)
    end
    output = output .. ",\n"
end

并且在循环内部添加额外的if语句以检查数组键:

for k,v in pairs(object) do
   if type(k) ~= "number" or k < 1 or k > largest or math.floor(k) ~= k then
       -- if type(v) == "number" then
       -- ...
   end
end

如果你将修改后的TableParser函数应用于以下表格:

local t = {
  1, 2, 3,
  value = "x",
  tab = {
    "a", "b", field = "y"
  }
}
print( TableParser( "", t ) )

则输出为:

{
    1,
    2,
    3,
    tab = {
        "a",
        "b",
        field = "y"
    },
    value = "x"
}

但是正确地处理表格序列化是棘手的。例如,你的实现未处理循环或表格作为键。请参见Lua Wiki以获取一些实现。

2014-12-28 10:49:46
用户3204551
用户3204551

你总是可以使用 pairsipairs 遍历表格,无论是否有意义。

  • ipairs 遍历表格中存在的序列(指以 1 开始的连续整数键,直到第一个缺失的值),除非被元方法 __ipairs (5.2)覆盖。

  • pairs 使用 next 遍历表格中所有键值对(因此顺序不确定),除非被元方法 __pairs (5.2)覆盖。

这意味着 ipairs 通常不会枚举 pairs 不显示的键值对。

而且没有办法验证 ipairs 是否会枚举所有 pairs 枚举的键,只能全部枚举并手动测试。

顺便说一下:你可以创建自己的迭代器,首先遍历序列,然后遍历其他所有键值对:

function my_iter(t)
    local k, cap
    return function()
        local v
        if k == nil then k, cap = 0 end
        if not cap then
            k = k + 1
            v = t[k]
            if v ~= nil then return k, v end
            cap, k = k
        end
        repeat k, v = next(k)
        until type(k) ~= "number" or 0 < k and k < cap and math.ceil(k) == k
        return k, v
    end
end

不过最好还是排序键以获得美观的输出:

function sorted_iter(t)
    local keys, index = {}, 0
    for k in next, t do
        keys[#keys + 1] = k
    end
    table.sort(keys)
    return function()
        index = index + 1
        local k = keys[index]
        return k, t[k]
    end
end
2014-12-28 11:05:51