根据条件将向量拆分为子向量的 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
假设我有一个向量 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