如果仅修改字段值,`next`会以某种稳定顺序产生吗?

问题描述

我正在生成一个字符串,以表示表中编码的状态。通过更改某些表条目的值,状态将发生变化。我将永远不会(在调用下面的mkName()之后)向表中添加密钥或从中删除键。

我希望字符串成为表中状态的一种“规范名称”,因此,如果我更改表中的值然后又将其改回,我希望该“名称”再次相同。当前,我直接在表上使用pairs()来读取表中的所有值。该遍历顺序在值更改时是否稳定?

我的代码如下

local function mkName(t)
  local name = ""
  for k,v in pairs(t) do name = name .. k .. ":" .. v .. "," end
  return name
end
local state = { a=1,b=2,c=3 }
local name1 = mkName(state)
state.b = 10
local name2 = mkName(state)
state.b = 2
local name3 = mkName(state)
-- Here I want name1 and name3 to be equal
print(name1,name2,name3,name1==name3)

这在我想要的意义上是有效且稳定的,但是next()(由pairs()使用)的官方文档说:

即使对于数字索引,也未指定索引的枚举顺序。 (要按数字顺序遍历表,请使用数字作为或ipairs函数。)

对于随后调用pairs()的迭代顺序可能有什么改变吗?

解决方法

我建议您不要依赖未承诺但可能会意外运行的内容,但要保证状态函数的遍历顺序稳定。可以这样完成:

-- Iterator to replace pairs():
local function ordered_pairs (t)
    -- Get table keys:
    local keys = {}
    for key,_ in pairs (t) do
        table.insert (keys,key)
    end
    -- Sort them:
    table.sort (keys,function (a,b)
        local type_a,type_b = type (a),type (b)
        return type_a < type_b
            or type_a == type_b and (type_a == 'number' or type_a == 'string') and a < b
            or type_a == type_b and tostring (a) < tostring (b)
    end)
    -- Loop over the sorted keys:
    local counter = 1
    return function()
        local key = keys [counter]
        if key then
            counter = counter + 1
            return key,t [key]
        end
    end
end
            
-- Actually,it's a serialiser:
local function hash (t)
    local serialised = {}
    for key,value in ordered_pairs (t) do
        table.insert (serialised,tostring (key) .. ':' .. tostring (value))
    end
    return table.concat (serialised,',')
end

-- Test:
local t = {a = 1,b = 2,c = 3}
print ('Stage 1 (b = 2):',hash (t))
t.b = 10
print ('Stage 2 (b = 10):',hash (t))
t.b = 2
print ('Stage 3 (b = 2):',hash (t))

首先对键进行排序(考虑所有可能的键类型,排序功能相当复杂)。然后按表的键顺序对其进行迭代。

如果需要,可以使用表的元表和pairsordered_pairs覆盖表的__pairs