Returning neighbours of a cell in a given matrix F#

166 views Asked by At

I have written a small snippet for extracing any neighbours of a given cell in a NxN matrix. Like so

let getNeighbours (x,y) (matrix: 'a [,]) = 
    let lower n = max 0 (n - 1)
    let upper n = min (matrix.GetUpperBound(0)) (n + 1)
    matrix.[lower x..upper x, lower y..upper y]

val arr : int [,] = [[29; 42; 0; 46; 55; 79; 18; 8]
                     [94; 25; 20; 45; 88; 73; 51; 69]
                     [62; 38; 66; 21; 55; 30; 37; 95]
                     [13; 35; 91; 0; 80; 15; 81; 22]
                     [2; 45; 94; 28; 50; 50; 35; 64]
                     [67; 98; 94; 63; 32; 11; 83; 23]
                     [38; 71; 31; 45; 52; 20; 20; 98]
                     [5; 4; 33; 19; 87; 17; 28; 78]]

> getNeighbours (4,0) arr;;
val it : int [,] = [[13; 35]
                    [2; 45]
                    [67; 98]]

Now it works as expected, I'm a bit unhappy about the way F# Interactive will display 2D arrays (it flips them so the X-axis will be displayed vertically while the Y-axis will be displayed horizontally) but aside from that no complaints.

However I can't figure out how to exclude the given cell from the neighbours in a concise manner, assuming that that the value of each cell in the matrix could hold the same value, ergo the only unique identifier of a given cell will be it's index.

2

There are 2 answers

1
Gus On BEST ANSWER

Here's another way, based on your proposed solution but using sequence expressions:

let getNeighbours (x,y) (matrix: 'a [,]) = 
    let lower n = max 0 (n - 1)
    let upper n = min (matrix.GetUpperBound(0)) (n + 1)
    seq {
        for i = lower x to upper x do
            for j = lower y to upper y do
                if (i, j) <> (x, y) then
                    yield matrix.[i, j]}
0
Overly Excessive On

The way I ended up going with was flattening the array converting it to a map and removing the first occurence of the given cell. It's not as concise or pretty as I would have hoped, perhaps someone else will have a better solution.

let getNeighbours (x,y) (matrix: 'a [,]) = 
    let flatten (arr: ((int * int) * 'a) [,]) = 
        arr |> Seq.cast<((int * int) * 'a)>
    let lower n = max 0 (n - 1)
    let upper n = min (matrix.GetUpperBound(0)) (n + 1)
    let hmap = matrix.[lower x..upper x, lower y..upper y] 
               |> Array2D.mapi (fun i j value -> ((i,j), value))
               |> flatten
               |> Map.ofSeq
    hmap.Remove (Map.findKey (fun key value -> value = matrix.[x, y]) hmap)
    |> Map.fold (fun acc _ value -> acc |> List.append [value]) []