F#如何搜索和计算事件直到一点?

时间:2018-07-10 15:55:47

标签: f#

我制作了一个数据结构来表示房屋迷宫。它的工作方式是,一条路径可能导致存在蛋糕,冰淇淋或烤箱的死角,或者可能导致左,右,双(左和右路径)或三重(左,中和右路径)。

这是房屋的类型和布局

type path =
|End of string
|Double of path * path
|Triple of path * path * path
|Left of path
|Right of path

let GingerbreadHouse = Triple(
                        Double( Double(End("*"), End("X")), Left(Right(End("X"))) ),     
                        Left( Double(End("*") , Left( Double(End("X") , Right(End("O"))))))  ,    
                        Left( Triple(Right(End("X")) , Double( Double(End("*") , End("X")) , End("X")) , Double(End("X") , Right(End("*"))) )) 
                        )

现在我要做的是计算到达烤箱之前先到达最右边的路径的蛋糕数量。

我首先尝试使用一个简单的辅助功能来完成此操作,该功能可以跟踪计数,当到达烤箱时,它将重新运行计数。但是我碰壁了,因为我无法以这种方式准确地返回死角。

let YummyKids house =
let rec helper house count =
    match t with
    |Left(p) -> helper p count
    |Right(p) -> helper p
    |Double(lp,rp) -> helper count + helper count
    |Triple(lp, fp, rp) -> helper rp count + helper mp count + helper lp count
    |End(treat) when treat = "*" -> helper ??? count
    |End(treat) when treat = "X" -> helper ??? (count+1)
    |End(treat) when treat = "O" -> count     
helper house 0 

所以我的第二次尝试是我会坚持使用普通的向后递归方法,但是我碰到了另一堵墙,因为我不知道如何在到达烤箱后真正结束整个过程。

let YummyKids house =
match t with
|Left(p) -> YummyKids p
|Right(p) -> YummyKids p
|Double(lp,rp) -> YummyKids rp + YummyKids lp
|Triple(lp, fp, rp) -> YummyKids rp + YummyKids mp + YummyKids lp
|End(treat) when treat = "*" -> 0
|End(treat) when treat = "X" -> 1
|End(treat) when treat = "O" -> //???

如果要计算整个迷宫中的蛋糕数量,这会起作用,但是我只想计数直到某个点,直到它到达烤箱。我该怎么办?

2 个答案:

答案 0 :(得分:4)

您的递归函数的结果需要指出到目前为止您发现了多少个蛋糕,还需要指出该过程是否已经到达烤箱,因此应该终止。

然后,您可以实施分支,以便在尚未找到烤箱的情况下继续到其他分支(加上蛋糕数),但是当在一个分支中找到烤箱时立即返回-在查看另一分支之前分支。

在下面,返回类型为int * bool,其中int代表蛋糕的数量,booltrue。有趣的情况是Double的处理:

let rec YummyKids path =
  match path with
  | Left(p) -> YummyKids p
  | Right(p) -> YummyKids p
  | Double(lp,rp) -> 
      let cakes, finished = YummyKids rp
      if finished then cakes, finished else
        let moreCakes, finished = YummyKids lp
        cakes + moreCakes, finished
  | End(treat) when treat = "*" -> 0, false
  | End(treat) when treat = "X" -> 1, false
  | End(treat) when treat = "O" -> 0, true

Double中,我们首先查看右边的分支,如果找到烤箱,则返回到目前为止的蛋糕数。如果为finished = false,我们将查看右侧分支并添加蛋糕。

我没有执行Triple情况,但是您应该能够按照与Double情况相同的模式很容易地完成该工作。

答案 1 :(得分:3)

Tomas已回答您的问题。我只想指出一些可能的优化方法:

  • 单个参数函数表达式后紧跟在参数let func arg = match arg with...上的匹配项可以替换为模式匹配函数let func = function...
  • 您可以将其与文字匹配,例如"X""O"true,从而可以避免使用关键字when的保护模式匹配规则
  • Triple案例可以由两个Double案例构成
  • 可以将绑定到同一模式的多个案例组合成只指定一次右手表达式的模式
  • 要使模式匹配完成并避免出现警告,请指定 通配符模式_
  • 可以在不带括号的情况下给出单个参数的情况

因此我们也可能会得出以下结论:

let rec YummyKids = function
| Double(lp,rp) ->
    match YummyKids rp with
    | _, true as result -> result
    | cakes, _ ->
        let moreCakes, finished = YummyKids lp
        cakes + moreCakes, finished
| Triple(lp,mp,rp) -> YummyKids (Double(Double(rp,mp),lp))
| Left p | Right p -> YummyKids p
| End "X" -> 1, false
| End "O" -> 0, true
| End _ -> 0, false