遍历F#2D阵列

时间:2011-04-27 08:26:36

标签: f#

我正在用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
    3. 否则,数字应为4
  3. 这应该创建一个新的数组,如下所示:

    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 个答案:

答案 0 :(得分:4)

我认为我在这个解决方案中没有任何偷偷摸摸的噱头。我使用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#interactive进行测试:

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

答案 1 :(得分:2)

我的一些想法:

Array旨在通过索引进行访问。如果您使用数组来存储数据,那么通过索引访问它是最方便的方法。

您想要的是一个数据结构,其中元素知道其邻居的位置。此数据结构不是简单的数组。您可以通过扩充数组来设计这样的数据结构:

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)

当您希望序列或列表知道其邻居时,会出现同样的问题。他们的设计不知道。如果列表中的元素知道其先前的节点,则它不再是单个链接列表。

当您选择数据结构时,您将继承其优势和简洁性。对于数组,您可以获得快速索引,而其元素本身没有位置概念。其他数据结构,比如线程二叉树或双链表,它们的元素知道它们的位置。但成本是使用更多存储。