Neven

Posted on

# Tuple transpose

Today I wrote a function and had some trouble figuring out what name should I give it, until realizing that it's a tuple transpose of sorts!

This is the implementation:

``````getindexer(i::Int) =
let i = i
coll -> coll[i]
end

transpose(
tuple::NTuple{n, <:NTuple{m, <:Any}}) where {n, m} =
ntuple(
let t = tuple
i -> map(getindexer(i), t)
end,
Val{m}())
``````

When interpreting a tuple of equally-sized tuples as a matrix, the `transpose` function returns the transpose of such a matrix.

Here are two example inputs and outputs:

``````julia> matrix2x3 = ((1, 2, 3), (4, 5, 6))
((1, 2, 3), (4, 5, 6))

julia> matrix3x3 = ((:aa, :ab, :ac), (:ba, :bb, :bc), (:ca, :cb, :cc))
((:aa, :ab, :ac), (:ba, :bb, :bc), (:ca, :cb, :cc))

julia> transpose(matrix2x3)
((1, 4), (2, 5), (3, 6))

julia> transpose(matrix3x3)
((:aa, :ba, :ca), (:ab, :bb, :cb), (:ac, :bc, :cc))
``````

The transpose is a self-inverse:

``````julia> matrix2x3 == transpose(transpose(matrix2x3))
true
``````

The implementation allows Julia to infer the return types as expected:

``````julia> using Test

julia> matrix5x6 = ntuple(i -> ntuple(j -> i*100 + j, 6), 5)
((101, 102, 103, 104, 105, 106), (201, 202, 203, 204, 205, 206), (301, 302, 303, 304, 305, 306), (401, 402, 403, 404, 405, 406), (501, 502, 503, 504, 505, 506))

julia> @inferred transpose(matrix5x6)
((101, 201, 301, 401, 501), (102, 202, 302, 402, 502), (103, 203, 303, 403, 503), (104, 204, 304, 404, 504), (105, 205, 305, 405, 505), (106, 206, 306, 406, 506))

julia> @inferred transpose(transpose(matrix5x6))
((101, 102, 103, 104, 105, 106), (201, 202, 203, 204, 205, 206), (301, 302, 303, 304, 305, 306), (401, 402, 403, 404, 405, 406), (501, 502, 503, 504, 505, 506))
``````

JET checks pass, too.

``````julia> using JET

julia> @report_opt transpose(matrix5x6)
No errors detected

julia> @report_opt transpose(transpose(matrix5x6))
No errors detected
``````

A much better implementation to extend the base function transpose. Much cleaner and easier to implement with one function inside the other function.

``````import Base: transpose

function transpose(tuple::NTuple{n, <:NTuple{m, <:Any}}) where {n, m}
function getindexer(i::Int)
coll -> coll[i]
end
ntuple(
let t = tuple
i -> map(getindexer(i), t)
end,
Val{m}()
)
end

matrix2x3 = ((1, 2, 3), (4, 5, 6))

zzz = transpose(matrix2x3)
``````

Neven

That would be type piracy.

Comment deleted