minus
minus

Reputation: 61

how to get the Column of a matrix in Ocaml

I want to print out the column of a matrix but i keep getting an error.

Error: This expression has type 'a list but an expression was expected of type int

let rec get_column2 mat x = match mat with
| [] -> raise (Failure "empty list")
| h::t -> if x = 1 then h else get_column2 t (x-1);;

let rec get_column mat x= match mat with
| [] -> raise (Failure "empty list")
| []::tv -> get_column tv x
| hv::tv -> get_column2 hv x::get_column tv x;;    

Matrix example [[2;5;6];[3;5;3][3;6;8]]

The first part works fine on type int list so I added the second part to go through the int list list and cut them into int list's and then tryed to get the columns of each separately.

I also tryed it this way:

let rec get_column mat x = 
    let rec column matr y =
        if matr = [] then raise (Failure "empty list") else
            if y = 1 then List.hd matr else
                column (List.tl matr) y-1;
    in column (List.hd mat) x :: get_column (List.tl mat) x;;

The second example translates fine but then doesn't work. I get an Exception "tl". (I'm not sure the function nesting is done right since I'm just learning Ocaml).

Upvotes: 1

Views: 1596

Answers (2)

matrixanomaly
matrixanomaly

Reputation: 6967

get_column2 - your first function, works as it should. That is it will fetch the value of each row in the matrix. It's a good helper function for you to extract the value from a list.

Your second function get_column gets all the types right, and you're accumulating everything, except that instead of stopping when you have an empty list [] you end up throwing an exception. That is your matrix example will go through just nicely, until it has no more lists to go through, then it will always throw the exception. (because the recursion keeps going till it's an empty list, and Ocaml will do as you told it too, fail when it gets an empty list.

The only thing you were missing was the exception, instead of throwing an exception, just return an empty list. That way your recursion will go all the way and accumulate till it's an empty list, and at the final step where the matrix is empty, it will append the empty list to the result, and you're golden.

So your code should be:

let rec get_column2 mat x = match mat with
| [] -> raise (Failure "empty list")
| h::t -> if x = 1 then h else get_column2 t (x-1)

let rec get_column mat x= match mat with
| [] -> [] (*doesn't throw an exception here*)
| []::tv -> get_column tv x
| hv::tv -> (get_column2 hv x)::get_column tv x

Instead of throwing the exception when it's an empty list, maybe you could check if the value of x is more than the length of the inner list.

Also, here's my implementation of doing it. It's still fairly basic as it doesn't use List.iter which everyone loves, but it doesn't rely on any additional packages. It makes use of nested functions so you don't expose them everywhere and pollute the namespace.

(*mat is a list of int list*)
let get_col mat x = 
    let rec helper rows x =  (*helper function that gets the value at x*)
     match rows with
     | [] -> raise (Failure "empty list")
     | h::t -> if x = 1 then h else helper t (x-1)
    in 
  let rec wrapper mat= (*function that walks through all the rows*)
    match mat with
    | [] -> [] 
    | rows::tl -> (helper rows x)::(wrapper tl) (*keep accumulating value*)
  in wrapper mat

How you can visualize the [] -> [] part is that when the recursion is at it's final stage (mat is reduced to an empty list), the wrapper function returns the empty list, which will be appended to the recursion stack (since we are accumulating the values in a list as per (helper rows x)::(wrapper tl)), and the recursion will complete.

You don't hit this error with your get_column2 as you tell ocaml to stop recursing and return a value when x=1.

Edit, Additional:

As Jeffrey mentioned, a much more elegant way of handling the error is adding the case for [row], where row is the last row in the matrix. You just return (helper row x) there. And you could have the empty matrix as a failure.

Example using your code:

let rec get_column mat x= match mat with
    | [] -> raise (Failure "empty list") (*fail here, as we don't want to compute a matrix with no rows.*)
    | [tv] -> get_column tv x (*just return the value*)
    | hv::tv -> (get_column2 hv x)::get_column tv x

Upvotes: 1

Jeffrey Scofield
Jeffrey Scofield

Reputation: 66823

When I try your first example, I don't get a type error. When I run it, I get the "empty list" failure. So your description of your problem seems wrong.

If you want to treat an empty matrix as an error, you must be very careful to handle a 1 x n matrix as your base case. I don't see that in your code.

Upvotes: 0

Related Questions