根据条件将向量拆分为子向量的 Julia 惯用方法

Julia idiomatic way to split vector to subvectors based on condition

假设我有一个向量 a = [1, 0, 1, 2, 3, 4, 5, 0, 5, 6, 7, 8, 0, 9, 0],我想根据该数组中值的条件将其拆分为更小的向量。例如。值为零。 因此我想获得以下向量的向量

 [1, 0]
 [1, 2, 3, 4, 5, 0]
 [5, 6, 7, 8, 0]
 [9, 0]

到目前为止,这对我来说是一个天真的解决方案,但它失去了类型。

function split_by_λ(a::Vector, λ)
    b = []
    temp = []
    for i in a
        push!(temp, i)
        if λ(i)
            push!(b, temp)
            temp = []
        end
    end
    b
end
split_by_λ(a, isequal(0))

然后我尝试玩了一下ranges,感觉比较地道一点,又不失类型。

function split_by_λ(a::Vector, λ)
    idx = findall(λ, a)
    ranges = [(:)(i==1 ? 1 : idx[i-1]+1, idx[i]) for i in eachindex(idx)]
    map(x->a[x], ranges)
end

split_by_λ(a, isequal(0))

但是这么简单的事情还是觉得很麻烦。 有没有我遗漏的东西,更简单的方法?

也许有人有更简短的想法,但这是我的想法:

julia> inds = vcat(0,findall(==(0),a),length(a))


julia> getindex.(Ref(a), (:).(inds[1:end-1].+1,inds[2:end]))
5-element Array{Array{Int64,1},1}:
 [1, 0]
 [1, 2, 3, 4, 5, 0]
 [5, 6, 7, 8, 0]
 [9, 0]
 []

或者如果您想避免复制 a

julia> view.(Ref(a), (:).(inds[1:end-1].+1,inds[2:end]))
5-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}:
 [1, 0]
 [1, 2, 3, 4, 5, 0]
 [5, 6, 7, 8, 0]
 [9, 0]
 0-element view(::Array{Int64,1}, 16:15) with eltype Int64

与 Przemyslaw 的回答几乎相同,但可能更少 隐秘 密集:

function split_by(λ, a::Vector)
    first, last = firstindex(a), lastindex(a)
    splits = [first-1; findall(λ, a); last]
    s1, s2 = @view(splits[1:end-1]), @view(splits[2:end])
    return [view(a, i1+1:i2) for (i1, i2) in zip(s1, s2)]
end

此外,我将签名更改为常规的“功能优先”签名,这样您就可以使用 do-blocks。此外,这应该与偏移索引一起使用。

肯定可以摆脱中间分配,但我认为没有 yield 会变得很丑陋:

function split_by(λ, a::Vector)
    result = Vector{typeof(view(a, 1:0))}()
    l = firstindex(a)
    r = firstindex(a)
    while r <= lastindex(a)
        if λ(a[r])
            push!(result, @view(a[l:r]))
            l = r + 1
        end
        r += 1
    end
    push!(result, @view(a[l:end]))

    return result
end