Hello!
When not using broadcasting, there is a way to destructure results from a function:
julia> f(x) = (x, x^2);
julia> (y1, y2) = f(5);
julia> y1
5
julia> y2
25
However, this does not work while broadcasting:
julia> x = 1:10
1:10
julia> (z1, z2) = f.(x);
julia> z1
(1, 1)
julia> z2
(2, 4)
Is there any way to do the same, and have in z1
and z2
arrays with the collection of the first and second arguments of the result respectively?
Sadly, no. The feature you want is unzip
. There is a PR here which has not gotten merged into Base.
There is a package for this, though: Unzip.jl.
1 Like
jling
3
notice this produces:
julia> f.(1:10)
10-element Vector{Tuple{Int64, Int64}}:
(1, 1)
(2, 4)
(3, 9)
(4, 16)
(5, 25)
(6, 36)
(7, 49)
(8, 64)
(9, 81)
(10, 100)
which is not ideal storage in memory to begin with if you’re interested in the “columns” (in this display). you can do:
julia> [x^y for x in 1:10, y in 1:2]
10Ă—2 Matrix{Int64}:
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81
10 100
Sadly, there’s no syntax for multi dimensional comprehension (probably would be too confusing)
You can also do this with StructArrays.jl
:
julia> using StructArrays
julia> using StructArrays: components
julia> z1, z2 = components(StructArray(f.(1:10)))
([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 4, 9, 16, 25, 36, 49, 64, 81, 100])
edit: this naturally suggests
unzip(f, x) = components(StructArray(f(xᵢ...) for xᵢ in x))
which works well for multidimensional iterators:
julia> g(x, y) = x*y, y/x
g (generic function with 1 method)
julia> v1, v2 = unzip(g, Iterators.product(1:3, 1:4))
([1 2 3 4; 2 4 6 8; 3 6 9 12], [1.0 2.0 3.0 4.0; 0.5 1.0 1.5 2.0; 0.3333333333333333 0.6666666666666666 1.0 1.3333333333333333])
julia> v1
3Ă—4 Matrix{Int64}:
1 2 3 4
2 4 6 8
3 6 9 12
1 Like
There is also Destruct.jl:
using Destruct
f(x) = (x, x^2)
x = 1:10
z1, z2 = f.(x) |> destruct
This yields
z1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
z2 = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]