Hi,
Sometimes in my code I need to iterate through the Cartesian power of a vector. After searching on the web I can come up with two approaches.
- Using product and repeated from Base.Iterators. The problem is that this approach produces type-unstable code.
Example:
using Base.Iterators
function test_base_iterators(n, k)
x = collect(1:n)
for p in product(repeated(x, k)...)
println(p)
end
end
Output of @code_warntype:
julia> @code_warntype test_base_iterators(3, 2)
MethodInstance for test_base_iterators(::Int64, ::Int64)
from test_base_iterators(n, k) in Main at REPL[1]:1
Arguments
#self#::Core.Const(test_base_iterators)
n::Int64
k::Int64
Locals
@_4::Union{Nothing, Tuple{Any, Union{Bool, Tuple}}}
x::Vector{Int64}
p::Any
Body::Nothing
1 ─ %1 = (1:n)::Core.PartialStruct(UnitRange{Int64}, Any[Core.Const(1), Int64])
│ (x = Main.collect(%1))
│ %3 = Main.repeated(x, k)::Base.Iterators.Take{Base.Iterators.Repeated{Vector{Int64}}}
│ %4 = Core._apply_iterate(Base.iterate, Main.product, %3)::Base.Iterators.ProductIterator
│ (@_4 = Base.iterate(%4))
│ %6 = (@_4 === nothing)::Bool
│ %7 = Base.not_int(%6)::Bool
└── goto #4 if not %7
2 ┄ %9 = @_4::Tuple{Any, Union{Bool, Tuple}}
│ (p = Core.getfield(%9, 1))
│ %11 = Core.getfield(%9, 2)::Union{Bool, Tuple}
│ Main.println(p)
│ (@_4 = Base.iterate(%4, %11))
│ %14 = (@_4::Union{Nothing, Tuple{Any, Tuple{Any, Vararg{Any}}}} === nothing)::Bool
│ %15 = Base.not_int(%14)::Bool
└── goto #4 if not %15
3 ─ goto #2
4 ┄ return nothing
- A hack using Combinatorics.multiset_permutations. This approach yields type-stable code but requires duplicating the input.
Example:
using Combinatorics
function test_combinatorics(n , k)
x = collect(1:n)
duplicates = append!(x, x)
for p in multiset_permutations(duplicates, k)
println(p)
end
end
Output of @code_warntype:
julia> @code_warntype test_combinatorics(3, 2)
MethodInstance for test_combinatorics(::Int64, ::Int64)
from test_combinatorics(n, k) in Main at REPL[2]:1
Arguments
#self#::Core.Const(test_combinatorics)
n::Int64
k::Int64
Locals
@_4::Union{Nothing, Tuple{Vector{Int64}, Vector{Int64}}}
duplicates::Vector{Int64}
x::Vector{Int64}
p::Vector{Int64}
Body::Nothing
1 ─ %1 = (1:n)::Core.PartialStruct(UnitRange{Int64}, Any[Core.Const(1), Int64])
│ (x = Main.collect(%1))
│ (duplicates = Main.append!(x, x))
│ %4 = Main.multiset_permutations(duplicates, k)::Combinatorics.MultiSetPermutations{Vector{Int64}}
│ (@_4 = Base.iterate(%4))
│ %6 = (@_4 === nothing)::Bool
│ %7 = Base.not_int(%6)::Bool
└── goto #4 if not %7
2 ┄ %9 = @_4::Tuple{Vector{Int64}, Vector{Int64}}
│ (p = Core.getfield(%9, 1))
│ %11 = Core.getfield(%9, 2)::Vector{Int64}
│ Main.println(p)
│ (@_4 = Base.iterate(%4, %11))
│ %14 = (@_4 === nothing)::Bool
│ %15 = Base.not_int(%14)::Bool
└── goto #4 if not %15
3 ─ goto #2
4 ┄ return nothing
Could anybody point me to the principled way to do what I want in Julia?