【问题标题】:Haskell Maze solving algorithmHaskell 迷宫求解算法
【发布时间】:2014-09-25 09:45:41
【问题描述】:

我正在尝试基于 Haskell 中以下链接中描述的算法实现迷宫求解器。

http://www.cs.bu.edu/teaching/alg/maze/

我对haskell和函数式编程很陌生,我基本上尝试按照链接中描述的方式编写算法,我尝试在网上浏览许多其他资源,但我被困在目标时步行停止的部分(它不会停止,它会回到原点)到达,我无法取消标记迷宫中的错误位置。

迷宫的样子

.########
.......#.
#.####..#
#.#######
...#.G...
##...####
#..######

我的代码如下

       findPath :: Int->Int->Maze->(Bool,Maze)
       findPath x y maze
            | not (isSafe maze x y) = (False,maze)
            | isChar maze x y 'G' = trace ("Solution: "++ show maze)(True,maze)
            | isChar maze x y '#' = (False,maze)
            | isChar maze x y '!' = (False,maze)
            | fst walkNorth = (True,markedList)
            | fst walkEast = (True,markedList)
            | fst walkSouth = (True,markedList)
            | fst walkWest = (True,markedList)
            | otherwise = (False,unMarkedList)
                where markedList = replaceCharInMaze maze x y '+'
                      unMarkedList = replaceCharInMaze maze x y '!'
                      walkNorth = findPath x (y-1) markedList
                      walkEast = findPath (x+1) y markedList
                      walkSouth = findPath x (y+1) markedList
                      walkWest = findPath (x-1) y markedList

isSafe 函数只检查边界,isChar 只是在给定 x,y 位置匹配的字符,replaceCharInMaze 函数用提供的字符替换 x,y 位置的字符。

isSafe :: [String]->Int->Int->Bool
isSafe list x y
    | y >= 0 && y < length (head list) && x >= 0 && x < length list && (isChar list xy '.' || isChar list x y 'G') = True
    | otherwise = False

所以,我有两个问题

  1. 我无法将在否则情况下进行的取消标记持续到下一次递归调用,我如何继续保持迷宫的状态,以便即使是未标记的状态也是一部分解决方案?

  2. 那么随着算法的进行,一直走到目标又回到起点,如何阻止这种情况的发生?

由于我是 Haskell 和算法的新手,我查看了诸如状态单子之类的主题,这似乎是解决方案,但我不太确定是否继续进行,我还尝试查看其他堆栈溢出帖子,但找不到任何可以帮助我的东西。

trace语句中得到的迷宫输出如下

+++#..###..#.
.#+++#+++.##.
####+#+#+#-##
...#+++#+#...
.#.####++.##.
.#G+++++#....
.############
....####.....

但它并不止于此,它会回溯到原点并将输出打印为

+..#..###..#.
.#...#....##.
####.#.#.#.##
...#...#.#...
.#.####...##.
.#G.....#....
.############
....####.....

【问题讨论】:

    标签: haskell recursion functional-programming maze


    【解决方案1】:

    当您使用示例运行程序时会发生以下情况。前四个守卫显然是假的,所以到那时为止没有发生太多事情。它通过递归一次来评估walkNorth,以便发现fst walkNorth 也是False。然后它评估walkEast,这需要一段时间,因为最终会导致目标。它发现fst walkEast 是True,所以它返回(True,markedList)。重要的是要意识到返回对中的 markedList 仅被“标记”一次(因此在您的输出中只有一个“+”)。在通往目标的路上已经发生了许多“标记”,但是从程序返回其输出的位置看不到这些标记。每次您将markedList 传递给walkXXX 函数之一时,您实际上是在创建一个带有附加标记的新列表,该标记只能在您将其传递到的函数调用中看到。你真正想要的是在它被解决的地方有标记的迷宫。在一天结束时,walkXXX 函数要么返回 (False,maze),当朝 XXX 方向行走时不会导致目标(因为第 1 或第 3 后卫的评估结果为真),或者 (True,maze) 如果它确实领先到目标(第二后卫评估为真),在这种情况下,maze 将具有所有正确的标记。因此,对于 fst walkXXX 案例,不要返回 markedList,而是返回 snd walkXXX。即

        | fst walkNorth = (True,snd walkNorth)
        | walkEast = (True,snd walkEast)
        | walkSouth = (True,snd walkSouth)
        | walkWest = (True,snd walkWest)
    

    您的第一个问题有点复杂。我认为您想要的是将您的 walkXXX 定义更改为大致如下:

    walkNorth = findPath x (y-1) markedList
    walkEast = findPath (x+1) y (replaceCharInMaze markedList x (y-1))
    walkSouth = findPath x (y+1) (replaceCharInMaze (replaceCharInMaze markedList x (y-1)) (x+1) y)
    

    我会让你填写最后一个。如果您向东走,您知道您已经尝试向北走但没有找到目标,因此您可以取消标记,依此类推。 (这不太行得通,至少因为它可能会尝试替换迷宫外的墙壁和角色,但想法就在那里。)

    您似乎还不习惯 Haskell 缺乏可变状态和频繁递归。其他一些事情(我不确定这些):我认为您的 otherwise 案例从未运行过,它并没有真正做任何事情——试着把它拿出来看看会发生什么;我也不认为你的(True,markedList) 对中的Trues 有任何影响——尝试将它们更改为False。

    【讨论】:

    • 嗨@ackien,感谢您的回复,这很有帮助,实际上我错过了一点,只有当您无法向任何方向移动时,您才应该使用 ! 取消标记单元格,但其他更改使其现在可以工作:),它停止在解决方案上,但取消标记仍然无法正常工作。
    • walkNorth = findPath' x (y-1) (replaceCharInMaze maze x y '+') walkEast = findPath' (x+1) y (replaceCharInMaze maze x y '+') walkSouth = findPath' x (y+1) (replaceCharInMaze maze x y '+') walkWest = findPath' (x-1) y (replaceCharInMaze (replaceCharInMaze maze x y '!') x y '+')
    • 解决方案如果 G 可达 +++#--###--#----# -##+++#+++-##---#-- ####+#+#+---#--#-- +++#+++#+###G+-#-- +#+####++-###+#### +#++++++##---+++++ +################+ ++++++++++++++++++ 如果它不可达则输出为 !--#--###--#----#- ##---#----##---#-- ####-#-#----#--#-- ---#---#-###@--#-- -#-####---######## -#------##-------- -################- ------------------
    • 嗨@ackien,我终于让它工作了,正如你所说,我抛弃了其他情况并添加了一个“not (fst walkWest)”条件,它返回到北方,非常感谢你的帮助! :-)
    猜你喜欢
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-08
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    相关资源
    最近更新 更多