2011-04-27 24 views
5

Estoy creando un juego de tablero (tipo) en F #, y estoy teniendo problemas para recorrer la matriz de una manera "funcional".Atravesar una matriz F # 2D

que tienen una matriz que se ve, por ejemplo, como:

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]] 

quiero crear una nueva matriz basada en la anterior, donde:

  1. Si el artículo es> 0, a continuación, en la nueva matriz debe ser el número 1
  2. Si el artículo es = 0, entonces:
    1. Si un elemento a la izquierda o la derecha o por encima o por debajo es> 1, a continuación, en las nuevas arra y el número debe ser de 2
    2. lo contrario, si un elemento a la izquierda de la derecha o por encima o por debajo es = 1, entonces en la nueva matriz El número debe ser 3
    3. lo contrario, el número debe ser 4

esto debería crear una nueva matriz que se parece a:

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]] 

no puedo ver ninguna manera simple en C# de lograr esto. En C# solo crearía un loop for para hacer esto, pero pensé que podría haber una forma furtiva de hacerlo en F #, usando cosas como las funciones map - mapi parecía prometedor, pero parece que solo da acceso a la pieza actual bajo consideración y sus índices, no toda la matriz ...

Mi problema parece ser que las reglas del juego dependen del área circundante, mientras que los métodos de cruce convencionales no parecen dar acceso a los alrededores área: ¿cuál es la mejor manera de lograr lo que estoy haciendo?

Respuesta

4

No creo haber hecho ningún truco engañoso en esta solución. Usé Array2D.init para construir una nueva matriz. La función requerida por init determina el valor de la matriz en cada posición.

Además, pongo la reunión de los vecinos en una función separada. En la función vecinos, utilicé una lista de comprensión y obtuve solo vecinos válidos, evitando problemas alrededor de las esquinas y los bordes.

Esto es lo que ocurrió con (advertencia: para que no falle cuando el 2D-array es 1x1 o vacío, lo dejaré al lector para protegerse contra este caso):

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 
     ) 

prueba en Fa # interactivo:

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 

resultado:

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]] 
+0

Esto se ve exactamente como lo que necesito, y se parece mucho más a F # de lo que estaba creando ... Mientras que mi solución era bastante similar, creé una nueva matriz llena de ceros, luego tuve un ciclo anidado para iterar la matriz original. Tampoco estaba usando esa lista de cosas de comprensión, así que probablemente no estaba tan cerca de esto en absoluto como para pensarlo ... –

2

algunos de mis pensamientos:

Array está diseñado para acceder a través de la indexación. Si usa una matriz para almacenar sus datos, acceder a ella a través de la indexación es la forma más conveniente.

Lo que quiere es una estructura de datos en la que un elemento sepa dónde están sus vecinos. Esta estructura de datos no es una simple matriz.Se puede diseñar una estructura de datos mediante el aumento de una matriz:

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

y también se puede agregar explícitamente los cuatro vecinos a un elemento:

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) 

El mismo problema surge cuando se desea una secuencia o una lista para conocer a sus vecinos Están diseñados para no saber. Si un elemento en una lista conoce su nodo anterior, entonces ya no es una sola lista vinculada.

Cuando elige una estructura de datos, hereda sus ventajas y su brevedad. Para array, obtiene indexación rápida, mientras que sus elementos no tienen una noción de posición. Otras estructuras de datos, por ejemplo, árboles binarios enhebrados o listas doblemente vinculadas, sus elementos saben dónde están. Pero el costo es que se usa más almacenamiento.

Cuestiones relacionadas