Lua中的pairs函数是否按照编写顺序返回结果?

5
有没有办法按照表格下面的顺序循环遍历表格?
local tbl = {
    ["hello"] = 1,
    [2] = 2,
    [50] = 3,
    ["bye"] = 4,
    [200] = 5
}

我的意思是当我使用“in pairs”时,每次执行代码时顺序都会不同...

我正在寻找像这样的东西:

function get_keys(tbl)
    local rtable = {}
    for k,v in pairs(tbl) do
        table.insert(rtable, k)
    end
    return rtable
end

local keys_of_tbl = get_keys(tbl)
for i = 1, table.getn(keys_of_tbl) do
    --Do something with: tbl[keys_of_tbl[i]]
end

但是因为函数“get_keys”再次基于“in pairs”,所以它将不起作用...


这些值是你想要循环的顺序吗?还是这只是巧合? - Etan Reisner
我想循环遍历表格“tbl”,并按照它的书写顺序进行,因此第一个遍历应该是关键字“hello”,第二个是2,第三个是50,... - Felix
2
根据源代码的顺序来依赖会在将来造成问题,尤其当有人不了解顺序的重要性而对代码进行重新排列时。如果你需要明确的顺序,请让顺序明确化。 - Etan Reisner
我为我的问题添加了另一个可行的解决方案 :) - Felix
2个回答

7
在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 -- new key 
    t._keys[#t._keys + 1] = k
  end
  if v == nil then -- delete key too.
    ordered_table.remove(t, k)
  else -- update/store value
    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

--- Example Usage:
local t = ordered_table.new{
  "hello", 1,  -- key, value pairs
  2, 2,
  50, 3,
  "bye", 4,
  200, 5
}

print(#t)
print("hello is", t.hello)
print()
for k, v in pairs(t) do  --- Lua 5.2 __pairs metamethod
  print(k, v)
end
t.bye = nil -- delete that
t[2] = 7 -- use integer keys
print(#t) 

1
通常情况下,没有人能够对他们的代码可能产生的所有不正确推断负责;有太多错误的方法可以做某件事情。我已经将搜索抽象出来,现在删除明显不在循环内部,希望这能满足我们双方的要求 :) - ryanpattison
这对我来说可以。=)谢谢你。如果我在github等地方看到这段代码,我不会评论它(或者根本不会想太多)。但是特别是在SO的背景下,以及该网站和其目标受众提供的帮助的长尾意图,我认为解决方案应该尽可能少地“负担”。 - Etan Reisner

5

没有"按照源代码书写"的表格顺序。(考虑到并非所有键都一定存在于源代码中)。对于非连续整数键,Lua没有"按顺序"的概念。

如果你想要一个特定的顺序,你需要手动以某种方式保留这个顺序。

如果你的表中没有任何整数键,则可以使用它们作为排序依据(并使用ipairs循环这些键,并将值的索引作为键来获取真实的值)。

如果您的原始值是您想要排序的顺序,则可以循环并反向映射以获取一个表,完成后可以使用ipairs迭代。


谢谢,我会走这条路...虽然不如理想中那么好,但如果没有其他选择,这将是唯一的方法 x.x - Felix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接