Lua 在迭代时安全地从数组表中删除项
这个问题类似,但明显不同 总结 给定一个Lua数组(表中的键是从Lua 在迭代时安全地从数组表中删除项,lua,Lua,这个问题类似,但明显不同 总结 给定一个Lua数组(表中的键是从1开始的顺序整数),迭代该数组并删除某些项的最佳方法是什么 现实世界的例子 我在Lua数组表中有一个时间戳条目数组。条目总是添加到数组的末尾(使用table.insert) 我需要偶尔(按顺序)浏览此表,并处理和删除某些条目: function processEventsBefore( timestamp ) for i,stamp in ipairs( timestampedEvents ) do if stamp[1
1
开始的顺序整数),迭代该数组并删除某些项的最佳方法是什么
现实世界的例子
我在Lua数组表中有一个时间戳条目数组。条目总是添加到数组的末尾(使用table.insert
)
我需要偶尔(按顺序)浏览此表,并处理和删除某些条目:
function processEventsBefore( timestamp )
for i,stamp in ipairs( timestampedEvents ) do
if stamp[1] <= timestamp then
processEventData( stamp[2] )
table.remove( timestampedEvents, i )
end
end
end
函数处理事件之前(时间戳)
对于i,在ipair(timestampedEvents)中盖章
如果stamp[1]我会想到,对于我的特殊情况,我只会从队列前面移动条目,我可以通过以下方式更简单地执行此操作:
function processEventsBefore( timestamp )
while timestampedEvents[1] and timestampedEvents[1][1] <= timestamp do
processEventData( timestampedEvents[1][2] )
table.remove( timestampedEvents, 1 )
end
end
函数处理事件之前(时间戳)
当timestampedEvents[1]和timestampedEvents[1][1]时,我突然想到,对于我的特殊情况,我只会从队列前面移动条目,我可以通过以下方式更简单地完成此操作:
function processEventsBefore( timestamp )
while timestampedEvents[1] and timestampedEvents[1][1] <= timestamp do
processEventData( timestampedEvents[1][2] )
table.remove( timestampedEvents, 1 )
end
end
函数处理事件之前(时间戳)
而timestampedEvents[1]和timestampedEvents[1][1]
在数组上迭代并从中间删除随机项,同时继续迭代的一般情况
如果您是前后迭代,当您删除元素N时,迭代中的下一个元素(N+1)会向下移动到该位置。如果您增加迭代变量(就像ipairs那样),您将跳过该元素。有两种方法可以解决这个问题
使用此示例数据:
input = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p' }
remove = { f=true, g=true, j=true, n=true, o=true, p=true }
我们可以通过以下方式在迭代过程中删除输入
元素:
从后向前迭代
for i=#input,1,-1 do
if remove[input[i]] then
table.remove(input, i)
end
end
手动控制循环变量,这样我们可以在删除元素时跳过递增:
local i=1
while i <= #input do
if remove[input[i]] then
table.remove(input, i)
else
i = i + 1
end
end
locali=1
而我
在数组上迭代并从中间删除随机项,同时继续迭代的一般情况
如果您是前后迭代,当您删除元素N时,迭代中的下一个元素(N+1)会向下移动到该位置。如果您增加迭代变量(就像ipairs那样),您将跳过该元素。有两种方法可以解决这个问题
使用此示例数据:
input = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p' }
remove = { f=true, g=true, j=true, n=true, o=true, p=true }
我们可以通过以下方式在迭代过程中删除输入
元素:
从后向前迭代
for i=#input,1,-1 do
if remove[input[i]] then
table.remove(input, i)
end
end
手动控制循环变量,这样我们可以在删除元素时跳过递增:
local i=1
while i <= #input do
if remove[input[i]] then
table.remove(input, i)
else
i = i + 1
end
end
locali=1
当我时,我会避开表。删除并遍历数组,一旦将不需要的条目设置为nil
,然后再次遍历数组,如有必要,将其压缩
下面是我想到的代码,使用Mud答案中的示例:
local input = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p' }
local remove = { f=true, g=true, j=true, n=true, o=true, p=true }
local n=#input
for i=1,n do
if remove[input[i]] then
input[i]=nil
end
end
local j=0
for i=1,n do
if input[i]~=nil then
j=j+1
input[j]=input[i]
end
end
for i=j+1,n do
input[i]=nil
end
我会避免使用表。删除并遍历数组,一旦将不需要的条目设置为nil
,然后再次遍历数组,必要时将其压缩
下面是我想到的代码,使用Mud答案中的示例:
local input = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p' }
local remove = { f=true, g=true, j=true, n=true, o=true, p=true }
local n=#input
for i=1,n do
if remove[input[i]] then
input[i]=nil
end
end
local j=0
for i=1,n do
if input[i]~=nil then
j=j+1
input[j]=input[i]
end
end
for i=j+1,n do
input[i]=nil
end
可以考虑使用A而不是排序数组。
当您按顺序删除条目时,优先级队列将有效地压缩自身
一个优先级队列实现的例子,请参阅此邮件列表线程:
,您可以考虑使用A代替排序数组。
当您按顺序删除条目时,优先级队列将有效地压缩自身
有关优先级队列实现的示例,请参阅此邮件列表线程:尝试此功能:
function ripairs(t)
-- Try not to use break when using this function;
-- it may cause the array to be left with empty slots
local ci = 0
local remove = function()
t[ci] = nil
end
return function(t, i)
--print("I", table.concat(array, ','))
i = i+1
ci = i
local v = t[i]
if v == nil then
local rj = 0
for ri = 1, i-1 do
if t[ri] ~= nil then
rj = rj+1
t[rj] = t[ri]
--print("R", table.concat(array, ','))
end
end
for ri = rj+1, i do
t[ri] = nil
end
return
end
return i, v, remove
end, t, ci
end
它不使用表。删除,因此它应该具有O(N)
复杂性。您可以将remove
函数移动到for生成器中,以消除对upvalue的需要,但这将意味着每个元素都有一个新的闭包。。。这不是一个实际问题
用法示例:
function math.isprime(n)
for i = 2, n^(1/2) do
if (n % i) == 0 then
return false
end
end
return true
end
array = {}
for i = 1, 500 do array[i] = i+10 end
print("S", table.concat(array, ','))
for i, v, remove in ripairs(array) do
if not math.isprime(v) then
remove()
end
end
print("E", table.concat(array, ','))
小心不要使用break
(或过早退出循环),因为它将使用nil
元素离开数组
如果希望break
表示“中止”(如中所示,不删除任何内容),可以执行以下操作:
function rtipairs(t, skip_marked)
local ci = 0
local tbr = {} -- "to be removed"
local remove = function(i)
tbr[i or ci] = true
end
return function(t, i)
--print("I", table.concat(array, ','))
local v
repeat
i = i+1
v = t[i]
until not v or not (skip_marked and tbr[i])
ci = i
if v == nil then
local rj = 0
for ri = 1, i-1 do
if not tbr[ri] then
rj = rj+1
t[rj] = t[ri]
--print("R", table.concat(array, ','))
end
end
for ri = rj+1, i do
t[ri] = nil
end
return
end
return i, v, remove
end, t, ci
end
这样做的优点是可以取消整个循环,而不删除任何元素,还可以提供跳过已标记为“待删除”的元素的选项。缺点是新表的开销
希望这些功能对您有所帮助。尝试以下功能:
function ripairs(t)
-- Try not to use break when using this function;
-- it may cause the array to be left with empty slots
local ci = 0
local remove = function()
t[ci] = nil
end
return function(t, i)
--print("I", table.concat(array, ','))
i = i+1
ci = i
local v = t[i]
if v == nil then
local rj = 0
for ri = 1, i-1 do
if t[ri] ~= nil then
rj = rj+1
t[rj] = t[ri]
--print("R", table.concat(array, ','))
end
end
for ri = rj+1, i do
t[ri] = nil
end
return
end
return i, v, remove
end, t, ci
end
它不使用表。删除,因此它应该具有O(N)
复杂性。您可以将remove
函数移动到for生成器中,以消除对upvalue的需要,但这将意味着每个元素都有一个新的闭包。。。这不是一个实际问题
用法示例:
function math.isprime(n)
for i = 2, n^(1/2) do
if (n % i) == 0 then
return false
end
end
return true
end
array = {}
for i = 1, 500 do array[i] = i+10 end
print("S", table.concat(array, ','))
for i, v, remove in ripairs(array) do
if not math.isprime(v) then
remove()
end
end
print("E", table.concat(array, ','))
小心不要使用break
(或过早退出循环),因为它将使用nil
元素离开数组
如果希望break
表示“中止”(如中所示,不删除任何内容),可以执行以下操作:
function rtipairs(t, skip_marked)
local ci = 0
local tbr = {} -- "to be removed"
local remove = function(i)
tbr[i or ci] = true
end
return function(t, i)
--print("I", table.concat(array, ','))
local v
repeat
i = i+1
v = t[i]
until not v or not (skip_marked and tbr[i])
ci = i
if v == nil then
local rj = 0
for ri = 1, i-1 do
if not tbr[ri] then
rj = rj+1
t[rj] = t[ri]
--print("R", table.concat(array, ','))
end
end
for ri = rj+1, i do
t[ri] = nil
end
return
end
return i, v, remove
end, t, ci
end
这样做的优点是可以取消整个循环,而不删除任何元素,还可以提供跳过已标记为“待删除”的元素的选项。缺点是新表的开销
我希望这些对您有帮助。我建议不要使用表。出于性能原因(可能或多或少与您的特定情况相关),请删除
下面是这种类型的循环对我来说通常是什么样子:
local mylist_size = #mylist
local i = 1
while i <= mylist_size do
local value = mylist[i]
if value == 123 then
mylist[i] = mylist[mylist_size]
mylist[mylist_size] = nil
mylist_size = mylist_size - 1
else
i = i + 1
end
end
当然,请记住,这取决于您的特定数据
以下是测试代码:
function test_srekel(mylist)
local mylist_size = #mylist
local i = 1
while i <= mylist_size do
local value = mylist[i]
if value == 13 then
mylist[i] = mylist[mylist_size]
mylist[mylist_size] = nil
mylist_size = mylist_size - 1
else
i = i + 1
end
end
end -- func
function test_mitch(mylist)
local j, n = 1, #mylist;
for i=1,n do
local value = mylist[i]
if value ~= 13 then
-- Move i's kept value to j's position, if it's not already there.
if (i ~= j) then
mylist[j] = mylist[i];
mylist[i] = nil;
end
j = j + 1; -- Increment position of where we'll place the next kept value.
else
mylist[i] = nil;
end
end
end
function build_tables()
local tables = {}
for i=1, 10 do
tables[i] = {}
for j=1, 100000 do
tables[i][j] = j % 15373
end
end
return tables
end
function time_func(func, name)
local tables = build_tables()
time0 = os.clock()
for i=1, #tables do
func(tables[i])
end
time1 = os.clock()
print(string.format("[%10s] elapsed time: %.3f\n", name, time1 - time0))
end
time_func(test_srekel, "srekel")
time_func(test_mitch, "mitch")
time_func(test_srekel, "srekel")
time_func(test_mitch, "mitch")
功能测试\u srekel(mylist)
本地mylist_大小=#mylist
局部i=1
虽然我我建议不要使用表。删除,因为性能原因(这可能或多或少与您的特定情况有关)
下面是这种类型的循环对我来说通常是什么样子:
local mylist_size = #mylist
local i = 1
while i <= mylist_size do
local value = mylist[i]
if value == 123 then
mylist[i] = mylist[mylist_size]
mylist[mylist_size] = nil
mylist_size = mylist_size - 1
else
i = i + 1
end
end
当然,请记住,这取决于您的特定数据
--helper function needed for remove(...)
--I’m not super able to explain it, check the link above
function isarray(tableT)
for k, v in pairs(tableT) do
if tonumber(k) ~= nil and k ~= #tableT then
if tableT[k+1] ~= k+1 then
return false
end
end
end
return #tableT > 0 and next(tableT, #tableT) == nil
end
function remove(targetTable, removeMe)
--check if this is an array
if isarray(targetTable) then
--flag for when a table needs to squish in to fill cleared space
local shouldMoveDown = false
--iterate over table in order
for i = 1, #targetTable do
--check if the value is found
if targetTable[i] == removeMe then
--if so, set flag to start collapsing the table to write over it
shouldMoveDown = true
end
--if collapsing needs to happen...
if shouldMoveDown then
--check if we're not at the end
if i ~= #targetTable then
--if not, copy the next value over this one
targetTable[i] = targetTable[i+1]
else
--if so, delete the last value
targetTable[#targetTable] = nil
end
end
end
else
--loop over elements
for k, v in pairs(targetTable) do
--check for thing to remove
if (v == removeMe) then
--if found, nil it
targetTable[k] = nil
break
end
end
end
return targetTable, removeMe;
function ArrayRemove(t, fnKeep)
local j, n = 1, #t;
for i=1,n do
if (fnKeep(t, i, j)) then
-- Move i's kept value to j's position, if it's not already there.
if (i ~= j) then
t[j] = t[i];
end
j = j + 1; -- Increment position of where we'll place the next kept value.
end
end
table.move(t,n+1,n+n-j+1,j);
--for i=j,n do t[i]=nil end
return t;
end
function ArrayRemove(t, fnKeep)
local i, j, n = 1, 1, #t;
while i <= n do
if (fnKeep(t, i, j)) then
local k = i
repeat
i = i + 1;
until i>n or not fnKeep(t, i, j+i-k)
--if (k ~= j) then
table.move(t,k,i-1,j);
--end
j = j + i - k;
end
i = i + 1;
end
table.move(t,n+1,n+n-j+1,j);
return t;
end