首页 > 解决方案 > 迭代列表时删除列表项时的 Julia BoundsError

问题描述

我想遍历一个列表并偶尔删除所述列表中的项目。下面是一个玩具示例:

function delete_item!(myarray, item)
    deleteat!(myarray, findin(myarray, [item]))
end

n = 1000
myarray = [i for i = 1:n];

for a in myarray
    if a%2 == 0
        delete_item!(myarray, a)
    end
end

但是我得到错误:

BoundsError: attempt to access 500-element Array{Int64,1} at index [502]

我怎样才能修复它(尽可能有效地)?

附加信息。以上似乎是一个愚蠢的例子,在我原来的问题中,我有一个交互的代理列表。因此,我不确定迭代副本是否是最佳解决方案。例如:

#creating my agent
mutable struct agent <: Any
    id::Int
end

function delete_item!(myarray::Array{agent, 1}, item::agent)
    deleteat!(myarray, findin(myarray, [item]))
end 

#having my list of agents
n = 1000
myarray = agent[agent(i) for i = 1:n];

#trying to remove agents from list while having them interact
for a in myarray
    #agent does stuff
    if a.id%2 == 0 #if something happens remove
        delete_item!(myarray, a)
    end
end

标签: julia

解决方案


不幸的是,这个问题没有单一的答案,因为最有效的方法取决于整个模型的逻辑(特别是其他代理的动作是否取决于实际从数组中删除某些条目的事实)。

在大多数情况下,以下方法应该是最简单的(我要离开findin,效率低下,但我知道您通常可能有重复myarray):

n = 1000
myarray = [i for i = 1:n];
keep = trues(n)

for (i, a) in enumerate(myarray)
    keep[i] || continue # do not process an agent that is marked for deletion
    if a%2 == 0 # here application logic might also need to check keep in some cases
        keep[findin(myarray, [a])] = false
    end
end

myarray = myarray[keep]

如果由于某种原因你真的需要myarray在每次迭代中删除元素,你可以这样做:

n = 1000
myarray = [i for i = 1:n];

i = 1
while i <= length(myarray)
    a = myarray[i]
    if a%2 == 0
        todelete = findin(myarray, [a])
        i -= count(x -> x < i, todelete) # if myarray has duplicates of a you have to move the counter back
        deleteat!(myarray, todelete)
    else
        i += 1
    end
end

一般来说,您提供的代码不会很快(例如,如果您知道myarray不包含重复代码,它可能会简单得多 - 我想您可以)。

编辑:如果您知道自己没有重复项(您可以简单地使用代理的索引 - 请注意我们也可以避免不必要的检查),这是您如何实现这两个版本的方法:

n = 1000
myarray = [i for i = 1:n];
keep = trues(n)

for (i, a) in enumerate(myarray)
    if a%2 == 0 # here application logic might also need to check keep in some cases
        keep[i] = false
    end
end

myarray = myarray[keep]

如果由于某种原因你真的需要myarray在每次迭代中删除元素,你可以这样做:

n = 1000
myarray = [i for i = 1:n];

i = 1
while i <= length(myarray)
    a = myarray[i]
    if a%2 == 0
        deleteat!(myarray, i)
    else
        i += 1
    end
end

推荐阅读