在Lua中插入和删除表元素

在Lua中插入和删除表元素,lua,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)

我不明白为什么下面的代码会产生错误

代码从底部的
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
当我运行代码时,我得到
尝试索引一个nil值(字段“?”)
错误

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


为什么我的代码不起作用以及如何修复?

根据5.1手册表格。删除“从表格中删除位置处的元素,如有必要,向下移动其他元素以关闭空间”


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

如Andrew所述,
对于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
的映射来代替它。

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

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

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

为了避免在迭代数组时删除字段所引起的问题,我使用了带有索引变量的
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

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

Andrew答对了。在迭代表时,切勿尝试删除表内的值。这是许多语言中的常见问题。通常,您会先存储索引,然后在循环之后删除元素。然而,你尝试做的很慢。为什么不简单地使用id作为表中的键?@modiX您能告诉我怎么做吗?我是新手。只需使用heads[id]=element添加新元素,使用heads[id]=nil再次删除具有给定id的元素。不需要任何缓慢的函数调用和迭代。@modiX首先,您必须确定表是否有,并且应该始终有一个序列。您的方法可能使表没有序列。@TomBlodget True,插入序列将丢失。如果应该保留这一点,那么最好维护一个id->i查找表,以便能够在不迭代表本身的情况下选择或删除元素。