关于解析:Lua:检查是否可以通过从0开始的对和ipair循环遍历表

Lua: Check if a table can be looped through via ipairs & ipairs starting at 0

我有一个不错的Lua表解析器,它可以打印出看起来很漂亮的Lua代码,我喜欢它...它的工作原理很漂亮。 有一点小问题...如果我要打印具有任何整数键的表或数组,它会使用pairs循环遍历它(不会讽刺地将代码弄乱),但我宁愿使用如果可能的话。 因此,我想知道是否可以检查一个表(无需物理查看),如果它可以使用ipairs首先遍历该表,否则可以使用对。 那么有没有办法从0开始而不是Lua的默认1开始循环?

Lua Table Parser(在Google上找到的基本代码,对其进行了更改,以使其打印出更友好的数组)...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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"") .."{" .."\
"

        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 ..",\
"

            end
        end
        return output .."\
"
.. tabs .."}"
    end
    return serialize(name, object, tabs)
end


So I want to know is it possible to check a table (without physically looking at it) if it can use ipairs to loop through it first else use pairs.

不要检查,只是做!首先使用ipairs并跟踪ipairs迭代器返回的最大密钥。然后使用pairs再次进行迭代,并忽略1ipairs中最大的键之间的所有整数键。

如果您确实要检查ipairs是否会执行某些操作,请查看表(rawget( object, 1 ) ~= nil)中的索引1。如果不对表进行迭代,则无法检查ipairs是否将覆盖表中的所有元素。

Then is there a way to start looping at 0 instead of Lua's default 1?

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

1
2
3
4
t = { 1, 2, 3, [ 0 ] = 0 }
for i,v in ipairs( t ), t, -1 do  -- only use first value returned by ipairs
  print( i, v )
end

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 ..",\
"

end

并在循环内添加一个附加的if语句以检查数组键:

1
2
3
4
5
6
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函数应用于下表:

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

输出为:

1
2
3
4
5
6
7
8
9
10
11
{
    1,
    2,
    3,
    tab = {
       "a",
       "b",
        field ="y"
    },
    value ="x"
}

但是正确地进行表序列化非常棘手。例如。您的实现不将循环或表作为键处理。有关某些实现,请参见Lua Wiki。


无论是否有意义,您都可以始终使用pairsipairs迭代表。

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

  • 除非使用元方法__pairs(5.2)覆盖,否则pairs会使用next遍历所有键值对(因此未指定顺序)。

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

而且没有办法验证ipairs是否会枚举所有键pairs会枚举,而是枚举所有内容并手动进行测试。

顺便说一句:您可以创建自己的迭代器,该迭代器首先对序列进行迭代,然后对其他所有事物进行迭代:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

尽管可能更好,但对漂亮打印的键进行排序:

1
2
3
4
5
6
7
8
9
10
11
12
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