我在Haskell写一个程序来模拟D& D中两个玩家之间的战斗。我使用的方法是将玩家存储为地图,然后攻击'根据攻击是否连接,接收播放器并返回具有更新运行状况的IO播放器的函数。我的所有代码似乎都按预期工作,直到最后一个函数combat
。 combat
应该是两个玩家,第一个攻击第二个,检查第二个是否已经死亡,如果没有,则按照玩家的顺序重复。相反,它似乎无限循环。
该功能本身如下 -
combat :: Player -> Player -> IO String
combat a b = do
damaged <- a `attacks` b
nextRound <- combat damaged a
return $ if (victor a damaged) /= "No victor yet" then (victor a damaged) else nextRound
attacks
函数需要两个玩家,检查第一个玩家的ID,并将适当的攻击序列应用于第二个玩家。当我在GHCi中单独使用它时它工作正常,所以我不认为那里有问题。
victor
函数需要两个玩家,检查其中任何一个是否已死(定义为HP <= 0),并返回描述结果的字符串。当我写了一个combat
的玩具版本而没有任何递归而刚刚返回&#34; test&#34;当条件没有得到满足时,它返回了一个组合&#34;玩家1获胜!&#34;并且&#34;测试&#34;,所以我不认为那里有问题。
我确实遇到了一个类似的问题,带有绑定,当有人像let n = n + 1
这样的代码时,有人遇到无限循环,但我不确定<-
是否适用于以同样的方式(或者,如果确实如此,我还能怎么写它)。
答案 0 :(得分:3)
如评论中所述,您需要在执行递归步骤之前执行终止检查,否则您将永远无法终止。这是一个小的重新排序:
combat :: Player -> Player -> IO String
combat a b = do
damaged <- a `attacks` b
case victor a damaged of
"No victor yet" -> combat damaged a
winner -> return winner
请注意,如果victor
返回Maybe String
,这样会更好,这样您就不会依赖这个神奇的字符串"No victor yet"
。然后你可以写:
combat :: Player -> Player -> IO String
combat a b = do
damaged <- a `attacks` b
case victor a damaged of
Nothing -> combat damaged a
Just winner -> return winner
答案 1 :(得分:2)
请尝试在将来发布最低可编译示例。
正如评论中所述,在combat
的主体内无条件地呼叫combat
是不可取的。相反,将它沉入if-then-else的正确臂中。
您还提到在其中一个案例中放置combat
时出现类型错误。我猜你正在使用return $ if ...
,这意味着任何一个分支都会被包裹在另一层IO ...
中,但你的combat
已经是IO _
类型。
combat :: Player -> Player -> IO String
combat a b = do
damaged <- a `attacks` b
if (victor a damaged) /= "No victor yet"
then return (victor a damaged)
else combat damaged a
现在还有另外两个疣。一个是调用victor
的重复语句。另一个是使用字符串比较来确定程序流,唉!相反,你如何让victor
返回ADT?
data Status = Dead String | Alive
combat :: Player -> Player -> IO String
combat a b = do
damaged <- a `attacks` b
case victor a damaged of
Alive -> combat damaged a
Dead s -> return s