#julia #idioms
#джулия #идиомы
Вопрос:
Допустим, у меня есть вектор 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))
Затем я попытался поиграть с диапазонами, которые кажутся немного более идиоматичными и не теряют тип.
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))
но это все еще кажется очень громоздким, поскольку это довольно простая задача.
Есть ли что-то, чего мне не хватает, какой-то более простой способ?
Ответ №1:
Может быть, у кого-то есть более короткая идея, но вот моя:
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
Комментарии:
1. Да, это кажется более юлианским, спасибо! Кроме того, не могли бы вы пояснить, почему нужно делать
Ref(a)
вместо того, чтобы просто передавать a? Я пытался сделать что-то подобное, транслируя через getindex, но я не понимаю, почему это не работает без передачи ссылки.2.
Ref(a)
позволяет избежать повторной трансляцииa
. Вы хотите транслировать по набору диапазонов полностьюa
каждый раз.
Ответ №2:
Почти такой же, как ответ Пшемыслава, но, возможно, меньше загадочный плотный:
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
-блоки. Кроме того, это должно работать со смещенной индексацией.
Можно было бы, конечно, избавиться от промежуточных распределений, но я думаю, что это становится уродливым без 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
Комментарии:
1. Я думаю, цель конкурса — сделать это в одной строке 😉
2. Я мог бы поклясться, что был пакет, делающий что-то подобное.
3. Я тоже не смог его найти. Я немного отредактировал свой код — возможно, он менее загадочный, а возможно, и нет