Обход F # 2D массива - PullRequest
       5

Обход F # 2D массива

5 голосов
/ 27 апреля 2011

Я создаю настольную (типовую) игру на F #, и у меня возникли проблемы с обходом массива «функциональным» способом.

У меня есть массив, который выглядит, например, как:

val myArray : int [,] = [[1; 1; 0; 0; 0]
                         [0; 0; 1; 0; 0]
                         [2; 0; 0; 0; 3]
                         [0; 0; 0; 0; 0]
                         [0; 1; 0; 0; 1]]

Я хочу создать новый массив на основе выше, где:

  1. Если элемент> 0, то в новом массиве должно быть число 1
  2. Если элемент = 0, то:
    1. Если элемент слева или справа или выше или нижеэто> 1, то в новом массиве число должно быть 2
    2. В противном случае, если элемент слева, справа или выше или ниже равен = 1, то в новом массиве число должно быть 3 * 1015.*
    3. В противном случае число должно быть 4

Это должно создать новый массив, который будет выглядеть так:

val myArray : int [,] = [[1; 1; 3; 4; 4]
                         [2; 3; 1; 3; 2]
                         [1; 2; 3; 2; 1]
                         [2; 3; 4; 4; 2]
                         [3; 1; 3; 3; 1]]

Я не вижулюбой простой способ в F # достижения этого.В C # я бы просто создал цикл for, чтобы сделать это, но я подумал, что в F # может быть хитрый способ сделать это, используя такие функции, как map функции - mapi выглядит многообещающе, но это только кажетсяпредоставить доступ к рассматриваемому фрагменту и его индексам, а не ко всему массиву ...

Моя проблема заключается в том, что правила игры зависят от окружающей области, тогда как стандартные методы обхода неКажется, я не могу получить доступ к окрестностям - какой лучший способ добиться того, что я делаю?

Ответы [ 2 ]

4 голосов
/ 27 апреля 2011

Я не думаю, что я вытащил какой-то подлый трюк в этом решении.Я использовал Array2D.init для создания нового массива.Функция, требуемая init, определяет значение массива в каждой позиции.

Кроме того, я поставил сбор соседей в отдельную функцию.В функции соседей я использовал понимание списка и получал только допустимых соседей, избегая проблем вокруг углов и краев.

Это то, что я придумал (предупреждение: он потерпит неудачу, когда 2D-массив равен 1x1 или пустЯ оставлю это для читателя, чтобы защититься от этого случая):

let neighbors r c (A:'a[,]) =
    [if r > 0 then yield A.[r-1,c]
     if r < Array2D.length1 A - 1 then yield A.[r+1,c]
     if c > 0 then yield A.[r,c-1]
     if c < Array2D.length2 A - 1 then yield A.[r,c+1]]

let newArray A =
    Array2D.init (Array2D.length1 A) (Array2D.length2 A) 
        (fun r c ->
            if A.[r,c] > 0 then 1 
            else
                match neighbors r c A |> List.max with
                | 1 -> 3
                | 0 -> 4
                | _ -> 2
        )

Тест на F # интерактив:

let myArray = array2D [[1; 1; 0; 0; 0]
                       [0; 0; 1; 0; 0]
                       [2; 0; 0; 0; 3]
                       [0; 0; 0; 0; 0]
                       [0; 1; 0; 0; 1]]

let result = newArray myArray

результат:

val result : int [,] = [[1; 1; 3; 4; 4]
                        [2; 3; 1; 3; 2]
                        [1; 2; 3; 2; 1]
                        [2; 3; 4; 4; 2]
                        [3; 1; 3; 3; 1]]
2 голосов
/ 27 апреля 2011

некоторые из моих мыслей:

Массив предназначен для доступа через индексацию.Если вы используете массив для хранения ваших данных, то доступ к ним с помощью индексации является наиболее удобным способом.

Вам нужна структура данных, в которой элемент знает, где находятся его соседи.Эта структура данных не является простым массивом.Вы можете спроектировать такую ​​структуру данных, увеличив массив:

type NArray(A: int [,]) = 
    member x.Item with get (i,j) = (A.[i,j], i, j, A)

, и вы также можете явно добавить четырех соседей к элементу:

type NArray(A: int [,]) = 
    let get i j di dj = 
        let newI = i + di
        let newJ = j + dj
        if newI < 0 || newI > A.GetLength(0) then None
        elif newJ < 0 || newJ > A.GetLength(1) then None
        else Some (A.[newI, newJ])

    member x.Item with get (i,j) = (A.[i,j], get i j 0 1, get i j 0 -1, get i j 1 0, get i j -1, 0)

Та же проблема возникает припоследовательность или список, чтобы знать своих соседей.Они предназначены, чтобы не знать.Если элемент в списке знает свой предыдущий узел, то он больше не является одним связанным списком.

Когда вы выбираете структуру данных, вы наследуете ее преимущества и краткость.Для массива вы получаете быструю индексацию, в то время как его элементы сами не имеют представления о положении.Другие структуры данных, скажем, резьбовые двоичные деревья или двойные связанные списки, их элементы знают, где они находятся.Но цена в том, что используется больше памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...