在Lua中,`pairs`遍历键的顺序是未指定的。但是你可以保存以数组方式添加项目的顺序,并使用`ipairs`(对于迭代数组中的键具有定义的顺序)遍历键。为此,您可以使用元表创建自己的有序表,因此在添加新键时将维护键顺序。
编辑(早期代码在更新时插入了多个键副本)
要实现这一点,可以使用`__newindex`,只要索引尚未添加到表中,就会调用该函数。`ordered_add`方法会更新、删除或将键存储在隐藏的表`_keys`和`_values`中。请注意,由于我们没有将值存储在表中,而是将其存储在“隐藏”的表`_keys`和`_values`中,因此在更新键时始终会调用`__newindex`。
但要注意,不能在此表中使用任何键,键名`"_keys"`将覆盖我们隐藏的表,因此更安全的替代方案是使用`ordered_table.insert(t, key, value)`,`ordered_table.index(t, key)`和`ordered_table.remove(t, key)`方法。
ordered_table = {}
function ordered_table.insert(t, k, v)
if not rawget(t._values, k) then
t._keys[#t._keys + 1] = k
end
if v == nil then
ordered_table.remove(t, k)
else
t._values[k] = v
end
end
local function find(t, value)
for i,v in ipairs(t) do
if v == value then
return i
end
end
end
function ordered_table.remove(t, k)
local v = t._values[k]
if v ~= nil then
table.remove(t._keys, find(t._keys, k))
t._values[k] = nil
end
return v
end
function ordered_table.index(t, k)
return rawget(t._values, k)
end
function ordered_table.pairs(t)
local i = 0
return function()
i = i + 1
local key = t._keys[i]
if key ~= nil then
return key, t._values[key]
end
end
end
function ordered_table.new(init)
init = init or {}
local t = {_keys={}, _values={}}
local n = #init
if n % 2 ~= 0 then
error"in ordered_table initialization: key is missing value"
end
for i=1,n/2 do
local k = init[i * 2 - 1]
local v = init[i * 2]
if t._values[k] ~= nil then
error("duplicate key:"..k)
end
t._keys[#t._keys + 1] = k
t._values[k] = v
end
return setmetatable(t,
{__newindex=ordered_table.insert,
__len=function(t) return #t._keys end,
__pairs=ordered_table.pairs,
__index=t._values
})
end
local t = ordered_table.new{
"hello", 1,
2, 2,
50, 3,
"bye", 4,
200, 5
}
print(#t)
print("hello is", t.hello)
print()
for k, v in pairs(t) do
print(k, v)
end
t.bye = nil
t[2] = 7
print(#t)