我不明白为什么以下代码会产生错误。
代码从底部的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}
。
为什么我的代码不起作用以及如何解决此问题?
答案 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
此方法允许您以升序迭代数组索引。