在迭代时从向量中删除元素

时间:2017-07-02 19:00:01

标签: julia

我的第一次尝试是:

I = Vector{String}(["first", "second", "third", "fourth"])

for i in I
    if i == "fourth"
        splice!(I, 4)
    end
    print("$i\n")
end

最终出现在Bounds错误中:

BoundsError(String["first", "second", "third"], (5,))

然后我觉得它有点像'#34;手写"方式:

I = Vector{String}(["first", "second", "third", "fourth"])

state = start(I)

while ! done(I, state)
    (i, state) = next(I, state)

    if i == "fourth"
        splice!(I, state - 1)
        print("Delete element i=$(state - 1)\n")
        state = state - 1
        continue
    end

    print("Got: i=$i state=$state\n")
end

输出:

Got: i=first state=2
Got: i=second state=3
Got: i=third state=4
Delete element i=4

但是,那不容易阅读也不容易写。有没有" julian"从迭代中删除元素的方法?或者是否有任何推荐的数据结构通过某些函数调用显式支持它?

2 个答案:

答案 0 :(得分:2)

解决方案1:使用shift!push!

julia> I = Vector{String}(["first", "second", "third", "fourth", "fifth"]);
julia> Inew = Vector{String}(0);
julia> while !isempty(I)
         i = shift!(I);
         if i == "fourth"; println("Skipped $i"); 
         else println("Got: i = $i"); push!(Inew, i);
         end
       end
Got: i = first
Got: i = second
Got: i = third
Skipped fourth
Got: i = fifth

julia> show(Inew)
String["first", "second", "third", "fifth"]

<小时/> 解决方案2:使用splice!

julia> I = Vector{String}(["first", "second", "third", "fourth", "fifth"]);
julia> i = 1;
julia> while i <= length(I)
         if I[i] == "fourth"; splice!(I,i);
         else i += 1;
         end
       end
julia> show(I)
String["first", "second", "third", "fifth"]

但是,请注意,这不一定更有效,因为无论何时拼接 ,都会为I分配新内存(因为它正在改变大小)。

解决方案3:使用findindeleteat!(即“单行”):

julia> I = Vector{String}(["first", "second", "third", "fourth", "fifth"]);
julia> deleteat!(I, findin(I, ["second", "fourth"]))
3-element Array{String,1}:
 "first"
 "third"
 "fifth"

如果你真的不需要执行任何其他中间操作(例如打印)而你只想识别和删除元素,那么这可能就是这样。

进一步宣传:

<子> 此外,关于您通过for循环执行此操作的尝试:使用for循环(使用任何语言)进行迭代时的基本规则是您在上迭代的变量的状态不会更改 。违反此规则通常会导致最佳情况出错,或者在最坏的情况下导致未定义的行为和无声错误。如果变量的状态意图改变,那么你不是在看'for循环'迭代,而是在更一般的while循环中,它不会假设一致的状态。

<子> 即你在这里做的是正确的方法,你不应该寻找一个涉及for循环的方法。 (如果你确实碰巧找到了一个,那就考虑一下这个糟糕的代码而不管它:p)。但是,是的,有更好看的方式;然而值得注意的是,你所做的基本上是重新发现轮子,因为你明确了for循环实际上依赖于julia的界面。即以下代码:

for i in MyCollection; print("$i "); end

<子> 基本上内部翻译为等效的:

state = start(MyCollection)
while !done( MyCollection, state)
  (i, state) = next(MyCollection, state)
  print("$i ")
end

答案 1 :(得分:1)

对于大型数组,多次调用splice!非常慢:

function d!(I, s)
    i = 1
    while i <= length(I)
        if I[i] == s; splice!(I,i);
        else i += 1;
        end
    end
end

function t()
    I = rand(1:10, 1000000)
    s = 1
    d!(I,s)
    I
end

julia> @btime t()
 6.752 s (2 allocations: 7.63 MiB)
899975-element Array{Int64,1}:
...

最好只使用迭代器调用deleteat!一次,该迭代器产生应删除的所有索引,例如,

function d2!(I, s)
    deleteat!(I, (i for i in eachindex(I) if I[i] == s))
end

function t2()
    I = rand(1:10, 1000000)
    s = 1
    d2!(I,s)
    I
end

julia> @btime t2()
 15.889 ms (8 allocations: 7.63 MiB)
900414-element Array{Int64,1}:
...