Can I directly use a tuple as an Array2D index in F#?

Viewed 69

Lets say I have an the following Array2D

> let arr = Array2D.init 2 3 (fun i j -> (i+1) * (j+2));;
> printfn "%A" arr
[[2; 3; 4]
 [4; 6; 8]]

I know I can access an array element like so

> arr[1, 2];;
8

But what if I have the coordinates saved in a tuple. Why can't I do the following or something similar:

> let coord = (1, 2);;
> arr[coord]
    Error: input.fsx (2,1)-(2,11) typecheck error This expression was expected to have type
    ''a[]'    
but here has type
    'int[,]'    

It feels kinda stupid to unpack the tuple each time before using it. Or is this the only way?

> let x, y = coord;;
> arr[x, y];;
8
4 Answers

You could always use ||> to unpack the tuple rather than using let expressions, along with Array2D.get. The downside is that its a lil more verbose for sure.

- let arr = Array2D.init 2 3 (fun i j -> (i+1) * (j+2))
- let coord = (1,2)
- coord ||> Array2D.get arr;;

val it : int = 8

I don't think there's any direct way to index a 2D array using a tuple. One similar alternative is to use a Map instead:

let map =
  seq {
    for i = 0 to 1 do
      for j = 0 to 2 do
        yield (i, j), (i+1) * (j+2)
  } |> Map

let coord = (1, 2)
map[coord]   // 8

F# treats tupled arguments in a special way. If method is defined not in F#, then it's arguments should be tupled:

System.String.Join(", ", [|1; 2; 3|])
System.String.Join ", " [|1; 2; 3|] // not valid

It's done to help overload resolution.

What you can do is to extend multidimensional array type:

type ``[,]``<'a> with
    member inline ar.Item with get ((x, y): (int*int)) : 'a =
        ar.[x, y]

And then use it:

let coord = (1, 2)
arr.Item coord |> printfn "%d"

Unfortunately arr.[coord] is not available and looks like a bug in compiler

All problems are solved through functions!

let xss = Array2D.init 2 3 (fun i j -> (i+1) * (j+2));;
let get arr (x,y) = Array2D.get arr x y

get xss (1,2) (* Returns 8 *)
Related