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
Top comments (3)
A much better implementation to extend the base function transpose. Much cleaner and easier to implement with one function inside the other function.
That would be type piracy.