我正在用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]]
我想根据上面的内容创建一个新数组,其中:
这应该创建一个新的数组,如下所示:
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
之类的东西很有希望,但它似乎只能访问正在考虑的当前作品及其索引,而不是整个阵列......
我的问题似乎是游戏规则依赖于周围区域,而标准的遍历方法似乎无法访问周围区域 - 实现我正在做的事情的最佳方式是什么?
答案 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)
当您希望序列或列表知道其邻居时,会出现同样的问题。他们的设计不知道。如果列表中的元素知道其先前的节点,则它不再是单个链接列表。
当您选择数据结构时,您将继承其优势和简洁性。对于数组,您可以获得快速索引,而其元素本身没有位置概念。其他数据结构,比如线程二叉树或双链表,它们的元素知道它们的位置。但成本是使用更多存储。