Julia BoundsError при удалении элементов списка во время итерации по нему - PullRequest
0 голосов
/ 11 мая 2018

Я хотел бы перебрать список и периодически удалять элементы из этого списка. Ниже пример игрушки:

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

1 Ответ

0 голосов
/ 12 мая 2018

К сожалению, нет однозначного ответа на этот вопрос, поскольку наиболее эффективный подход зависит от логики всей модели (в частности, действия других агентов зависят от того факта, что некоторая запись фактически удалена из массива).

В большинстве случаев следующий подход должен быть самым простым (я оставляю 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
...