如何转换集合中特定元素的状态?

时间:2016-03-15 23:32:03

标签: f#

如何转换集合中特定元素的状态?

我试图通过以下测试:

[<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 // ??

2 个答案:

答案 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。我会把你的这部分功课留给你: - )