在Lua中插入和删除表格元素

时间:2018-07-10 14:23:06

标签: lua

我不明白为什么以下代码会产生错误。

代码从底部的main()函数开始。

heads = {}
function push(t)
    if (#t == 2) then
        table.insert(heads, t)
    end
end
function remove(id)
    for i = 1, #heads do
        if (heads[i][2] == id) then
            table.remove(heads, i)
        end
    end
end
function main()
    push({50, 1})
    push({50, 2})
    push({50, 3})
    remove(2)
end

运行代码时,出现attempt to index a nil value (field '?')错误。

我希望将子表元素推入表中,然后仅删除第二个元素。因此,生成的元素可以是{50, 1}{50, 3}

为什么我的代码不起作用以及如何解决此问题?

4 个答案:

答案 0 :(得分:2)

根据5.1手册table.remove“从表中删除该位置的元素,必要时向下移动其他元素以关闭空间”

头的大小(#heads)在循环执行之前计算一次,当i == 2时,您调用table.remove,因此表的大小缩小为2,在下一次迭代中,您尝试索引头[3] [ 2],但是heads [3]为nil,因此出现“试图索引nil值”错误消息。

答案 1 :(得分:1)

如安德鲁所说,for i = 1, #heads do将到达列表的原始长度;如果您在循环中缩短heads,则最终迭代将读取heads[i]并仅找到nil

解决此问题的一种简单方法是在列表中向后移动 ,因为删除元素仅会影响从以下位置删除的索引

for i = #heads, 1, -1 do
    if heads[i][2] == id then
        table.remove(heads, i)
    end
end

请注意,在任何情况下,这都是O(n*d)的复杂性,如果要从列表中删除许多元素,可能会非常慢。而且,正如其他人指出的那样,有一种O(1)方法,您可以使用v[1] => v中的地图。

答案 2 :(得分:1)

安德鲁说得对。迭代表时,切勿尝试删除表中的值。这是许多语言中的常见问题。通常,您将先存储值,然后像这样删除:

local e
for i = 1, #heads do
    if (heads[i][2] == id) then
        e = i
    end
end
if e then table.remove(heads, e) end

但是,此解决方案很慢。只需将ID用作表的键即可:

local heads = {}

heads[1] = 50 -- push
heads[2] = 50
heads[3] = 50
heads[2] = nil -- remove

不需要不必要的函数调用和迭代。

答案 3 :(得分:0)

为避免在遍历数组时删除字段而导致的问题,我使用了带有索引变量的while循环,该循环在每次迭代结束时增加,但在删除索引时减少。因此,例如,删除所有具有偶数索引的元素:

local t = { 1, 2, 3, 4, 5 }
local i = 1
while t[i] do
  if t[i] % 2 == 0 then
    table.remove(t, i)
    i = i - 1
  end
  i = i + 1
end

此方法允许您以升序迭代数组索引。