Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/meteor/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 如何在网格中递归地识别特定类型的单元?_Recursion_F#_Tail Recursion - Fatal编程技术网

Recursion 如何在网格中递归地识别特定类型的单元?

Recursion 如何在网格中递归地识别特定类型的单元?,recursion,f#,tail-recursion,Recursion,F#,Tail Recursion,我正在学习F#,我正在构建一个扫雷舰应用程序。作为其中的一部分,我正在尝试一种方法,如果一枚地雷被反复引爆,我将引爆所有相邻的地雷。所以如果我有一个网格,比如: | 0 | 1 | 2 | ------------------------ 0 | E-1 | M | E-2 | 1 | E-2 | E-4 | M | 2 | M | E-3 | M | 我在0,1引爆地雷,它会在1,2引爆地雷,然后在2,2引爆地雷。2,0中的地雷不会引爆

我正在学习F#,我正在构建一个扫雷舰应用程序。作为其中的一部分,我正在尝试一种方法,如果一枚地雷被反复引爆,我将引爆所有相邻的地雷。所以如果我有一个网格,比如:

     |  0  |  1  |  2  |
------------------------
  0  | E-1 |  M  | E-2 |
  1  | E-2 | E-4 |  M  |
  2  |  M  | E-3 |  M  |
我在0,1引爆地雷,它会在1,2引爆地雷,然后在2,2引爆地雷。2,0中的地雷不会引爆,因为它与其他任何地雷都不相邻

此时,我实际上已将字段实现为一个列表:

module CellContents =
  type T = 
    | Empty of int
    | Mine
    | Crater

module Location =
  type T = { Row: int; Column: int }

module Cell =
  type T = 
    { Content : CellContents.T
      Location : Location.T }

module Field =
  type T = Cell.T list
我遇到的问题是如何从单元格0,1开始,最后列出相邻的所有地雷。因此,我需要一个列表,如(仅显示坐标):

我很容易找到一个特定位置的相邻矿山,然后从该组中确定矿山

我遇到的问题是让它以某种方式重现,直到附近没有发现地雷,给我一个需要引爆的地雷的主列表

一旦我得到了地雷的总清单,我就可以引爆它们,并建立一个更新的战场,让这些地雷变成陨石坑

更新 @凯文的答案奏效了,但我很难理解。如果其他人也有困难,我将添加下面的函数,带有注释和一些更改

let detonateProximity (field:T) (start:Cell.T) =
    let rec inner cells m acc =
        match cells with
        | [] -> acc
        | x::xs -> 
            match x.Content with
            |Mine ->
              match proximity m.Location x.Location with
              // Continue but don't accumulate
              | Self -> inner xs m acc
              | Near -> 
                  // See if current cell has already been found
                  match acc |> List.tryFind (fun t -> t = x) with
                  // If it has, we're done. Pass out
                  // an empty list which ends recursion.
                  |Some _ -> [] 
                  // If it wasn't found (this parts hurts my brain)...
                  // calls function once for rest field and then
                  // using new starting point on whole field.
                  // Is this efficient at all?
                  |None -> List.concat [(inner xs m (x::acc));(inner field x (x::acc))]
              // Don't accumulate, continue with rest of mines.
              | Far -> inner xs m acc
            // Not a mine, keep going, don't accumulate
            |_ -> inner xs m acc

    // The set ensures no duplicates
    Set.ofList (inner field start [])
接近
功能(未显示)简单地封装了确定被测水雷是参考水雷、靠近参考水雷还是远离参考水雷的逻辑。例如,如果当前单元格和我的单元格之间的距离为零{Row=0,Column=0},则返回
Self

好的,所以我不知道F#,所以我将用Python编写此代码

def getDetonationList ( startingWithThisCell ) :
    seen = set()
    current = set ( [startWithThisCell] )
    while current :
        # Get all the mine cells that are adjacent to every ones
        # we are currently exploring. Then remove the ones we're
        # currently exploring and the ones we've seen, just so
        # we don't duplicate effort later.
        nearby = allMinesAdjacentToCells(current) - seen - current

        # Mark that we've seen the current ones (add the current
        # ones to the overall seen ones
        seen.update ( current )

        # Now we start over, starting from the newly found mines
        current = nearby
        # Note that if this is empty, this list will end, which
        # means that we will have seen as many as we could have
        # seen.
    return seen

这将返回一组所有引爆的电池,包括启动连锁反应的电池

module Location =
  type T = {Row: int; Column: int }

  let subtract l1 l2 =
      {Row=l1.Row - l2.Row;Column=l1.Column-l2.Colum

let detonate (field:Field.T) (start:Cell.T) =
  let rec inner cells m acc =
    match cells with
    | [] -> acc
    | x::xs -> match x.Content with
               |Mine ->match subtract m.Location x.Location with
                       |{Row = 0;Column = 0} -> inner xs m acc
                       |loc when abs (loc.Row) < 2 && abs (loc.Column) < 2 -> 
                          match acc |> List.tryFind (fun t -> t = x) with
                          |Some _ -> [] 
                          |None -> List.concat [(inner xs m (x::acc));(inner field x (x::acc))]
                       | _ -> inner xs m acc
               |_ -> inner xs m acc
  Set.ofList (inner field start [])

好的,否决这一切你想要的,但是Python也是一种编写伪代码的优秀语言,它向你展示了如何做事情的途径,即使它没有复制和粘贴代码。@FoggyFinder,因为我不知道F#,但是“我如何递归地查找附近的单元格”不是一个语言问题:这是一个方法论问题(除非你用的是愚蠢的语言)@iAdjunct,虽然你认为这不一定是一个语言问题,但这是一个与如何从功能上实现这一点相关的问题。你的例子不是从功能上实现的,而是必须实现的。也就是说,你有一个变量,
seen
,你正在循环中更新。在功能性范式中,不会有循环,因为理想情况下,我不会更新变量,而是计算一个值。这是学习F#(或任何函数式编程语言)的重点:学会以不同的方式思考问题。啊,我不知道F#是一种函数语言。我想这就解释了“F”。为什么你不想使用2D数组而不是位置和单元格?好吧,最初的原因是努力工作列表操作,因为我猜在现实生活中我会使用它而不是数组。我还想知道如何使用F一个2d数组解决方案是可行的。我的理解是,如果我保持字段不变,我必须一直重新分配字段,或者我必须更改数组状态(即更改数组中的值)。我愿意接受关于这个和/或澄清的想法。主要的是,这个项目是为了学习如何使用F#,所以我有时会根据我将学到的东西而不是效率来做决定。谢谢!但是,因为我对函数式编程还是很陌生,我的大脑即将爆炸,试图摸索逻辑=:-o。你有没有可能d一些注释来解释发生了什么?我真的不理解
tryFind
匹配以及为什么在匹配为
None
时更新累加器。
module Location =
  type T = {Row: int; Column: int }

  let subtract l1 l2 =
      {Row=l1.Row - l2.Row;Column=l1.Column-l2.Colum

let detonate (field:Field.T) (start:Cell.T) =
  let rec inner cells m acc =
    match cells with
    | [] -> acc
    | x::xs -> match x.Content with
               |Mine ->match subtract m.Location x.Location with
                       |{Row = 0;Column = 0} -> inner xs m acc
                       |loc when abs (loc.Row) < 2 && abs (loc.Column) < 2 -> 
                          match acc |> List.tryFind (fun t -> t = x) with
                          |Some _ -> [] 
                          |None -> List.concat [(inner xs m (x::acc));(inner field x (x::acc))]
                       | _ -> inner xs m acc
               |_ -> inner xs m acc
  Set.ofList (inner field start [])
detonate board {Content=Mine;Location={Row=0;Column=1}}
  |> Set.map (fun t -> t.Location)
  |> Set.toList