在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查找表,以便能够在不迭代表本身的情况下选择或删除元素。