Julia Community 🟣

Neven
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}())
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

The transpose is a self-inverse:

julia> matrix2x3 == transpose(transpose(matrix2x3))
true
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

JET checks pass, too.

julia> using JET

julia> @report_opt transpose(matrix5x6)
No errors detected


julia> @report_opt transpose(transpose(matrix5x6))
No errors detected
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
ohanian profile image
Steven Siew

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)
Enter fullscreen mode Exit fullscreen mode