Given a matrix represented as a list of lists:

[[1, 2, 3, 4], [5, 6, 7, 8], [9,10,11,12]]

write a function that will transpose the matrix (that is, switch rows and columns). I’ve formatted the numbers so you can see what I mean more clearly.

[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Now, if this were your typical imperative language like C/C++, Java, you name it, you’d just write some nested for loops and the problem would be solved.

In Elixir, though, you are encouraged to think in terms of functions and recursion rather than iterative loops. The key to working with lists in a recursive way is to write your functions like this:

Divide the list into its first item (the head) and the remaining items (the tail).

Process the head of the list.

Now look at the remaining items. If there are no remaining items, your job here is done. If there are remaining items, well, they’re a list, too, just like the original list, only smaller. Simply call this function and pass it the tail of the list. (There’s your recursion.)



Here’s how this applies to the problem of transposing a matrix. Call a function and pass it the matrix as its first argument. The second argument to this function will be the “result so far,” which is an empty list at the beginning. The first thing the function does is to separate the head (the first row of the matrix) from the tail.

Process the head by flipping it on its side and attaching it to the result matrix. (For the moment, don’t worry about how this happens. We’ll get to it soon.)

Now that the head of the matrix is taken care of, call the function again and give it the tail of the matrix as the first argument and the result so far as the second argument:

The function once again has a matrix; just a slightly smaller one. It separates the head and tail of this matrix, and once again flips the head on its side and attaches it to the result.

…and then calls the function yet again with the remaining row and the new result as the arguments.

At some point, you won’t have any remaining rows, so whatever is in the result will be your transposed matrix.

This worked out to the following Elixir code. Since I haven’t addressed the problem of flipping a row on its side, the make_column/2 function is just a placeholder that adds the row, unchanged, to the start of the result.

defmodule Transpose1 do def transpose(m) do attach_row(m, []) end def attach_row([], result) do # handle ending case first result end def attach_row(row_list, result) do [first_row | other_rows] = row_list new_result = make_column(first_row, result) attach_row(other_rows, new_result) end def make_column(row, result) do [row | result] end end

If you put this code in a file named transpose1.ex , compile it, and call the function, you’ll get this result:

iex(1)> c("transpose1.ex") [Transpose1] iex(2)> Transpose1.transpose([[1,2,3,4], [5,6,7,8], [9,10,11,12]]) ['\t

\v\f',[5,6,7,8],[1,2,3,4]]

(The '\t

\v\f\' is really the list [9,10,11,12] ; iex displays it as a character list.)

Notice that the rows are in the reverse order because I put them in at the beginning of the result list. This happens a lot when you construct lists in Elixir.

The next question is how to flip a row on its side to make it into a column. You can handle this recursively as a function that takes a row as its first argument and the “result so far” as its second argument.

Divide the row into its first item (head) and remaining items (tail).

Divide the result into its first result row (head) and remaining result rows (tail).

Put the first item into the first row of the result.

That new first row is followed by the result of calling the function recursively with the remaining items and remaining result rows.

When you run out of remaining items in the row you’re trying to flip on its side, you have finished the task.

Here is what the three clauses of the new make_column/2 function do:

When there are no more entries in the row, the column you are making is complete.

Make the row into a column when the result matrix is empty. Create the first item as a singleton list. Follow it with the result of making the remaining entries in the column.

Make a row into a column when the result matrix is not empty. Do this by adding the first item at the beginning of the first row of the result. That list is followed by the result of making the remaining entries in the column

def make_column([], result) do # my job here is done result end def make_column(row, []) do [first_item | other_items] = row [[first_item] | make_column(other_items, [])] end def make_column(row, result) do [first_item | other_items] = row [first_row | other_rows] = result [[first_item | first_row] | make_column(other_items, other_rows)] end

Here’s what happens when you compile and run this new code:

iex(1)> c("transpose1.ex") /home/examples/article2/transpose1.ex:1: redefining module Transpose1 [Transpose1] iex(2)> Transpose1.transpose([[1,2,3,4], [5,6,7,8], [9,10,11,12]]) [[9,5,1],[10,6,2],[11,7,3],[12,8,4]]

Oops. When flipping a row, new items are added at the beginning of the result rows, so each row of the transposed matrix is in the wrong order. No problem; just write yet another recursive function to reverse each of the rows once all the input rows are processed.

The first clause of reverse_rows/2 prepends the reversed row to the reversed matrix.

The first clause finishes the recursion. Because I’m adding the reversed rows in reverse order (!!), I have to do one final Enum.reverse to set matters right when the job is done.

See if you can figure out how the reverse_rows/2 function’s recursion follows the pattern that I established at the beginning of this article.

def attach_row([], result) do reverse_rows(result, []) end def reverse_rows([], result) do Enum.reverse(result) end def reverse_rows(rows, result) do [first | others] = rows reverse_rows(others, [Enum.reverse(first) | result]) end

And voilà, it works: