如何转换集合中特定元素的状态?
我试图通过以下测试:
[<Test>]
let ``Any live cell with fewer than two live neighbors dies, as if caused by under-population``() =
// Setup
let rowCount = 3
let result = rowCount |> createGrid
|> cycle
|> cycle
|> getStatus (2,2)
// Verify
result |> should equal Dead
第一个循环将导致中心细胞存活:
[<Test>]
let ``center cell is the first to live``() =
// Setup
let rowCount = 3
let result = rowCount |> createGrid
|> cycle
|> getStatus (2,2)
// Verify
result |> should equal Alive
但是,第二个周期应该将规则应用于单元格网格中的每个单元格。
这是我被困的地方。具体来说,每当网格中某个单元格的状态发生变化时,我都不知道有适当的方法来转换网格。
请考虑以下代码:
let cycle (grid:Map<(int * int), Cell>) =
let isBeginnng = grid |> Map.forall (fun _ cell -> cell.State = Dead)
match isBeginnng with
| true -> grid |> setCell { X=2; Y=2; State=Alive }
| false -> grid // ?? idk
因此,每当细胞改变状态时,其他细胞也会通过改变状态作出反应。
结果,我发现自己在转动轮子。
完整代码在此处:
type State = Alive | Dead
type Cell = { X:int; Y:int; State:State }
type Response = | Die
| Survive
| Resurect
let isNeighbor cell1 cell2 =
let isAbsNeighbor v1 v2 =
match abs (v1 - v2) with
| 0 | 1 -> true
| _ -> false
let isValueNeighbor v1 v2 =
match v1 >= 0
&& v2 >= 0 with
| true -> isAbsNeighbor v1 v2
| _ -> isAbsNeighbor v2 v1
match cell1.X <> cell2.X
|| cell1.Y <> cell2.Y with
| true -> isValueNeighbor cell1.X cell2.X
&& isValueNeighbor cell1.Y cell2.Y
| _ -> false
let createGrid rowCount =
[for x in 1..rowCount do
for y in 1..rowCount do
yield { X=x; Y=y; State=Dead }
]|> List.map (fun c -> (c.X, c.Y), { X=c.X; Y=c.Y; State=Dead })
|> Map.ofList
let setCell cell (grid:Map<(int * int), Cell>) =
grid |> Map.map (fun k v -> match k with
| c when c = (cell.X, cell.Y) -> { v with State=cell.State }
| _ -> v)
let getStatus coordinate (grid:Map<(int * int), Cell>) =
match grid.TryFind coordinate with
| Some cell -> cell.State
| None -> Dead
let getNeighbors (coordinate:int*int) =
let x,y = coordinate
let west = x-1, y
let northWest = x-1, y+1
let north = x, y+1
let northEast = x+1, y+1
let east = x+1, y
let southEast = x+1, y-1
let south = x, y-1
let southWest = x-1, y-1
[west; northWest; north; northEast; east; southEast; south; southWest]
let cycle (grid:Map<(int * int), Cell>) =
let isBeginnng = grid |> Map.forall (fun _ cell -> cell.State = Dead)
match isBeginnng with
| true -> grid |> setCell { X=2; Y=2; State=Alive }
| false -> grid // ??
答案 0 :(得分:1)
我认为每次要更改数据时,都会在此处使用Map.map
创建新的Map
。毕竟,Map
不可变 。如果你想要一个可变的集合,你应该使用Dictionary
。但首先让我们得到一个函数,让我们在给定Cell状态和邻居状态的情况下更新状态。这是一个简单的方法,使你的细胞永远消亡。
let update (cell:Cell) (neighborState:State list) =
match cell.State with
| Alive -> {cell with State = Dead}
| Dead -> {cell with State = Dead}
然后我们想要更新cycle
函数的模式匹配以遍历网格,获取每个网格位置的邻居的状态,然后使用之前的更新函数适当地更新它。
let cycle (grid:Map<(int * int), Cell>) =
let isBeginnng = grid |> Map.forall (fun _ cell -> cell.State = Dead)
match isBeginnng with
| true -> grid |> setCell { X=2; Y=2; State=Alive }
| false -> grid |> Map.map (fun location cell -> location
|>getNeighbors
|> List.map ( fun neighbor -> getStatus neighbor grid)
|> update cell
)
这将根据您要更新的功能创建一个新的网格,其中包含新的更新值。由于这看起来像生活游戏,您可以通过更改update
函数来计算aliveNeighbors
列表中neighborState
的#,然后使用if语句。
注意还表明您的 getNeighbors
函数存在网格边缘的错误,例如getNeighbors (2,1)
返回[(1, 1); (1, 2); (2, 2); (3, 2); (3, 1); (3, 0); (2, 0); (1, 0)]
,其中{ {1}}不是有效的网格位置。
编辑:别介意你的getStatus函数实际上已经解释了这一点,我很蠢。
答案 1 :(得分:0)
在概念层面上扩展先前的答案:在较小的部分(即函数)中考虑这一点可能是有用的。那么,在给定网格的当前状态和坐标的情况下,如何更新单个单元?对于测试中的规则,您需要查看该坐标的邻居(即使用getNeighbors
),找出它们的状态(getStatus
),只收集那些活着的{({{{ 1}})然后计算它们(List.filter
)并根据它做出决定。
如果将所有这些字符串串在一起,则可以获得以下函数:
List.length
现在您知道如何更新一个单元格。现在,您的任务是将此更新应用于所有单元格,即let cycleOneCell grid coord cell =
let aliveNeighbours =
getNeighbors coord
|> List.filter (fun n -> getStatus n grid = Alive)
if List.length aliveNeighbours < 2 then
{ cell with State = Dead }
else
cell
,即您将使用Map
。我会把你的这部分功课留给你: - )