Haskell中的Board-traversing函数返回重复项

时间:2014-11-09 18:41:51

标签: algorithm haskell traversal

我正在努力在Haskell中实现Go(游戏),但我似乎无法弄清楚如何在棋盘上编写找到字符串(连接的宝石组)的函数。如果石头以直线或L形连接,我的工作原理,但如果它们是正方形,我开始重复,如果它是3x3正方形,它就会永远运行(或者可能只是很长一段时间)。如果有人想帮忙的话,完整的源代码就在这里(http://lpaste.net/113992)。据我所知,问题是函数将通过两条不同的路径到达同一个方格,例如。 (1,1) - > (2,1) - > (2,2)和(1,1) - > (1,2) - > (2,2),所以(2,2)被计算两次,这里是相关的代码:

stringOfPiece :: Piece -> Game -> [Piece]
stringOfPiece piece game = recursiveStringFinder piece game [piece]

recursiveStringFinder :: Piece -> Game -> [Piece] -> [Piece]
recursiveStringFinder piece game piecesFound 
    | null adjacentUnfoundPieces = [piece]
    | otherwise = concat [recursiveStringFinder adjacentPiece game ([piece] ++ adjacentUnfoundPieces ++ piecesFound) | adjacentPiece <- adjacentUnfoundPieces] ++ [piece]
    where adjacentUnfoundPieces = [adjacentPiece | adjacentPiece <- findAdjacentPieces piece game, not $ adjacentPiece `elem` piecesFound]

基本上stringOfPiece函数只能用额外的参数(piecesFound)来调用recursiveStringFinder。 recursiveStringFinder有三个参数;它目前所在的部分,包含所有部分的游戏,以及迄今已发现的所有部分的PieceFound参数。如果有邻居,则返回[piece](当前片段),并结合在adjacentPieces上调用recursiveStringFinder的结果。如果没有,则返回一个包含单个元素的列表(它当前所在的片段)。例如,如果董事会看起来像这样:

xxx
xxx
WWx

哪里&#39; w&#39;是白色,x是空的,它将从(1,1)开始,看它有一个邻居,并返回[recursiveStringFinder(2,1)game [(1,1)]] ++ [(1,1)]和recursiveStringFinder(2,1)游戏[(1,1)]将是[(2,1)],因为没有邻居,所以结果是[(1,1),(2,1)]。这个算法适用于上面的板,这些例子如下:

xxx    Wxx    xWW
Wxx    Wxx    xWx
WWx    WWW    WWW

基本上,当只有一种方法可以到达某一石头时,它才有效。如果给出类似的东西,它会出现重复:

WWx
WWx
xxx

我可以用“小块”去除重复,但当石块组变大(3x3,2x4等)时,会出现严重的减速问题。如何修改我的代码,以便同一石头不会多次到达?

1 个答案:

答案 0 :(得分:1)

我不擅长阅读功能代码,但我认为问题在于,您不会将不断增长的已找到作品列表从一个子调用的输出中导入下一个,这意味着例如通过第一次这样的呼叫找到的额外部分列表的知识对于其兄弟子问题是不可用的。目前,你的

[recursiveStringFinder adjacentPiece game ([piece] ++ adjacentUnfoundPieces ++ piecesFound) | adjacentPiece <- adjacentUnfoundPieces]

有效地尝试解决所有子问题&#34;并行&#34;,这是破碎的。

使用命令性代码很容易解决这个问题:您只需将可变 piecesFound数组(或哈希表等)的(引用)传递给{{1}函数:然后所有未来的调用都会自动看到对此的任何更新,包括后来的兄弟调用。要在功能代码中执行此操作,您需要更改解决子问题的方式,从使用该列表理解(假定子问题独立性)到显式强制执行该子问题,我依赖于已经解决的子问题i-1。我认为这将涉及用一个递归来替换列表理解,该递归采用子片段的列表(&#34;队列&#34;)来尝试,并且在每次调用中拉出第一个项目,测试它是否为#s; s已经看过了,如果没有,我会处理 - 但我不确定。