我正在尝试将这段c ++代码翻译成haskell但却无法真正理解它。这取自http://cs.stanford.edu/people/eroberts/courses/cs106b/chapters/07-backtracking-algorithms.pdf中的nim.cpp。
C ++:
const int MAX_MOVE = 3;
const int NO_GOOD_MOVE = -1;
int FindGoodMove(int nCoins) {
for (int nTaken = 1; nTaken <= MAX_MOVE; nTaken++) {
if (IsBadPosition(nCoins - nTaken)) return nTaken;
}
return NO_GOOD_MOVE;
}
bool IsBadPosition(int nCoins) {
if (nCoins == 1) return true;
return FindGoodMove(nCoins) == NO_GOOD_MOVE;
}
到目前为止,我在haskell做过:
findGoodMove nCoins = if isBadPosition (nCoins - nTaken) == True
then nTaken
else -1
where nTaken = 1
isBadPosition nCoins = if nCoins == 1
then True
else findGoodMove(nCoins) == -1
我陷入了for循环部分。我真的想要一些关于如何将其翻译成haskell的建议。
提前致谢。
答案 0 :(得分:11)
首先,我会使用Haskell的更丰富的类型系统来编码findGoodMove
中的失败,给它类型
findGoodMove :: Int -> Maybe Int
isBadPosition
可以有
isBadPosition :: Int -> Bool
接下来我们可以继续实施。为此,我需要从isNothing
Data.Maybe
import Data.Maybe
maxMove :: Int
maxMove = 3
findGoodMove :: Int -> Maybe Int
findGoodMove nCoins = loop 1
where
loop nTaken
| nTaken == maxMove = Nothing
| isBadPosition (nCoins - nTaken) = Just nTaken
| otherwise = loop (nTaken + 1)
isBadPosition :: Int -> Bool
isBadPosition 1 = True
isBadPosition nCoins = isNothing $ findGoodMove nCoins
因此,对Maybe Int
使用Int
而不是findGoodMove
,我们可以在类型系统级别编码,此函数具有单一故障模式(即NO_GOOD_MOVE
),我认为这样可以使意图更清晰,并确保我们不会在我们的代码中的其他位置意外地将NO_GOOD_MOVE
标志视为有效值。
对于循环部分,我已经定义了一个本地名为loop
的递归函数,该函数从1
开始,并且有3个分支。如果nTaken
达到移动的最大值,我们就会在循环结束时返回Nothing
(NO_GOOD_MOVE
)。如果该条件为False
,我们会检查nCoins - nTaken
是不是一个错误的位置,如果是,我们会返回Just nTaken
。如果这两个条件都不是True
,那么我们会使用nTaken + 1
再次执行循环。
对于isBadPosition
,我们知道如果nCoins == 1
,它是True
,那么我们可以使用模式匹配来处理这个简单的情况。否则,我们必须知道findGoodMove nCoins
是否成功,只有当Nothing
不成功时才会成功。 Data.Maybe.isNothing
功能在此处非常有用,可以快速检查Nothing
或Just something
是否正确,因此也可以轻松完成此案例。
答案 1 :(得分:2)
这样的东西?
max_move = 3
no_good_move = -1
findGoodMove nCoins = findGoodMoveStep nCoins 1
findGoodMoveStep nCoins nTaken = if nTaken> max_move
then no_good_move
else if isBadPosition (nCoins - nTaken) == True
then nTaken
else findGoodMoveStep nCoins nTaken+1
isBadPosition nCoins = if nCoins == 1
then True
else findGoodMove(nCoins) == no_good_move
答案 2 :(得分:0)
不是像在@bheklilr的答案中那样定义loop
函数,而是使用list comprehension,这是Haskell语言中的一个强大工具。然后,您可以使用模式匹配来正确选择nTaken
。
maxMove :: Int
maxMove = 3
findGoodMove :: Int -> Maybe Int
findGoodMove nCoins =
let choices = [nTaken | nTaken <- [1..maxMove], isBadPosition (nCoins - nTaken)]
in case choices of
[] -> Nothing
(nTaken:_) -> Just nTaken
isBadPosition :: Int -> Bool
isBadPosition 1 = True
isBadPosition nCoins = findGoodMove nCoins == Nothing
因为Haskell很懒。列表choices
不会完全构建。一旦找到第一个有效nTaken
,它就会停止。因此,函数findGoodMove
的时间复杂度与C ++版本相同。