is there a nice way to optionally include an element in a vector literal construction? something like foo = [1, 2, @maybe x 3, 4, 5]
where only if x==true
is 3 included.
answering myself, i suppose insert!
is good enough: foo=[1,2,4,5]; insert!(foo, 3, 3)
This may depend on what you mean by βliteralβ. insert!
is not really a literal syntax because a temporary array is getting constructed and then manipulated. For a true literal, we would want to achieve the result at construction.
julia> x = true
true
julia> foo = [y for (i,y) in pairs(1:5) if i!=3 || x]
5-element Vector{Int64}:
1
2
3
4
5
julia> x = false
false
julia> foo = [y for (i,y) in pairs(1:5) if i!=3 || x]
4-element Vector{Int64}:
1
2
4
5
julia> f(x) = [y for (i,y) in pairs(1:5) if i!=3 || x]
f (generic function with 2 methods)
julia> @code_lowered f(true)
CodeInfo(
1 β #33 = %new(Main.:(var"#33#35"))
β %2 = #33
β %3 = Main.:(var"#34#36")
β %4 = Core.typeof(x)
β %5 = Core.apply_type(%3, %4)
β #34 = %new(%5, x)
β %7 = #34
β %8 = 1:5
β %9 = Main.pairs(%8)
β %10 = Base.Filter(%7, %9)
β %11 = Base.Generator(%2, %10)
β %12 = Base.collect(%11)
βββ return %12
)
The array comprehension syntax gets lowered to several layers of lazy iterators before being collected. Only at collection is the array allocated and formed. Notably see Iterators.filter
which creates a Iterators.Filter
.
This also works:
using Base.Iterators
g(x) = collect(flatten([[1,2],x ? [3] : Int[],[4,5]]))
and
julia> g(false) == [1,2,4,5]
true
julia> g(true) == [1,2,3,4,5]
true
With this method, in case x
is false, the unincluded element is not even materialized or computed (especially important for complex elements).
Using Iterators.flatten
is nice. Do note that a type assertion might be good for the return type since the method is currently type unstable. The array comprehension is type stable.
julia> f(x) = [y for (i,y) in pairs(1:5) if i!=3 || x]
f (generic function with 2 methods)
julia> @code_warntype f(true)
MethodInstance for f(::Bool)
from f(x) in Main at REPL[114]:1
Arguments
#self#::Core.Const(f)
x::Bool
Locals
#50::var"#50#52"{Bool}
#49::var"#49#51"
Body::Vector{Int64}
1 β (#49 = %new(Main.:(var"#49#51")))
β %2 = #49::Core.Const(var"#49#51"())
β %3 = Main.:(var"#50#52")::Core.Const(var"#50#52")
β %4 = Core.typeof(x)::Core.Const(Bool)
β %5 = Core.apply_type(%3, %4)::Core.Const(var"#50#52"{Bool})
β (#50 = %new(%5, x))
β %7 = #50::var"#50#52"{Bool}
β %8 = (1:5)::Core.Const(1:5)
β %9 = Main.pairs(%8)::Core.Const(Base.Pairs(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5))
β %10 = Base.Filter(%7, %9)::Core.PartialStruct(Base.Iterators.Filter{var"#50#52"{Bool}, Base.Pairs{Int64, Int64, LinearIndices{1, Tuple{Base.OneTo{Int64}}}, UnitRange{Int64}}}, Any[var"#50#52"{Bool}, Core.Const(Base.Pairs(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5))])
β %11 = Base.Generator(%2, %10)::Core.PartialStruct(Base.Generator{Base.Iterators.Filter{var"#50#52"{Bool}, Base.Pairs{Int64, Int64, LinearIndices{1, Tuple{Base.OneTo{Int64}}}, UnitRange{Int64}}}, var"#49#51"}, Any[Core.Const(var"#49#51"()), Core.PartialStruct(Base.Iterators.Filter{var"#50#52"{Bool}, Base.Pairs{Int64, Int64, LinearIndices{1, Tuple{Base.OneTo{Int64}}}, UnitRange{Int64}}}, Any[var"#50#52"{Bool}, Core.Const(Base.Pairs(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5))])])
β %12 = Base.collect(%11)::Vector{Int64}
βββ return %12
julia> g(x) = collect(flatten(( (1,2), x ? (3,) : (), (4,5) )))
g (generic function with 1 method)
julia> @code_warntype g(true)
MethodInstance for g(::Bool)
from g(x) in Main at REPL[116]:1
Arguments
#self#::Core.Const(g)
x::Bool
Locals
@_3::Union{Tuple{}, Tuple{Int64}}
Body::Any
1 β %1 = Core.tuple(1, 2)::Core.Const((1, 2))
βββ goto #3 if not x
2 β (@_3 = Core.tuple(3))
βββ goto #4
3 β (@_3 = ())
4 β %6 = @_3::Union{Tuple{}, Tuple{Int64}}
β %7 = Core.tuple(4, 5)::Core.Const((4, 5))
β %8 = Core.tuple(%1, %6, %7)::Core.PartialStruct(Tuple{Tuple{Int64, Int64}, Union{Tuple{}, Tuple{Int64}}, Tuple{Int64, Int64}}, Any[Core.Const((1, 2)), Union{Tuple{}, Tuple{Int64}}, Core.Const((4, 5))])
β %9 = Main.flatten(%8)::Base.Iterators.Flatten
β %10 = Main.collect(%9)::Any
βββ return %10
Yes, youβre right, I thought the Int[]
in the definition would be enough for inference, but adding Vector{Int}
is enough:
g(x) = collect(flatten(Vector{Int}[[1,2],x ? [3] : Int[],[4,5]]))
and:
julia> @code_warntype g(false)
...
β %11 = Main.collect(%10)::Vector{Int64}
βββ return %11
Actually, the problem was with the tuples, which made flatten
unstable. Perhaps a good compromise is:
g(x) = collect(flatten(( (1,2), x ? [3] : Int[], (4,5) )))
which is type stable.