Haskell无限循环在做块

时间:2016-02-14 03:05:19

标签: haskell

我在Haskell写一个程序来模拟D& D中两个玩家之间的战斗。我使用的方法是将玩家存储为地图,然后攻击'根据攻击是否连接,接收播放器并返回具有更新运行状况的IO播放器的函数。我的所有代码似乎都按预期工作,直到最后一个函数combatcombat应该是两个玩家,第一个攻击第二个,检查第二个是否已经死亡,如果没有,则按照玩家的顺序重复。相反,它似乎无限循环。

该功能本身如下 -

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这样的代码时,有人遇到无限循环,但我不确定<-是否适用于以同样的方式(或者,如果确实如此,我还能怎么写它)。

2 个答案:

答案 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