我正在尝试解决“AI - 现代方法”一书中的问题2.8,该书涉及一组单元格并选择随机移动来导航网格。
2.7为n X m矩形房间实施环境,每个房间都有 square有5%的几率含有污垢,n和m被选中 随机范围从8到15,包括在内。
2.8为环境设计并实施纯反射剂 练习2.7,忽略回家的要求,并测量 它的表现。
所以我使用了两个状态monad - 一个以Grid
作为状态,另一个以StdGen
作为状态。代码编译没有任何错误,但是当我从GHCi运行它时,它会卡住并且不会返回。
代码的相关部分:
支持代码
type RandomState = State StdGen
makeGrid :: (Int, Int) -> (Int, Int) -> Float -> RandomState Grid
doAction :: Action -> Cleaner -> State Grid Cleaner
getRandomR :: Random a => (a, a) -> RandomState a
getRandomR limits = do
gen <- get
let (val, gen') = randomR limits gen
put gen'
return val
chooseAction :: Percepts -> RandomState Action
chooseAction percepts
| PhotoSensor `elem` percepts = return SuckDirt
| InfraredSensor `elem` percepts = return TurnOff
| TouchSensor `elem` percepts = return TurnLeft
| otherwise = do
r <- getRandomR ((1, 3) :: (Int, Int))
case r of
1 -> return GoForward
2 -> return TurnRight
3 -> return TurnLeft
主要代码
runCleaner :: Int -> Cleaner -> StateT Grid RandomState Cleaner
runCleaner turnsLeft cleaner@(Cleaner _ _ _ ph _) =
if turnsLeft == 0
then return cleaner
else do
grid <- get
gen <- lift $ get
cleaner <- case ph of
[] -> do
let (cleaner, grid) = runState (doAction GoForward cleaner) grid
put grid
return cleaner
_ -> do
let (action, gen) = runState (chooseAction (head ph)) gen
lift $ put gen
let (cleaner, grid) = runState (doAction action cleaner) grid
put grid
return cleaner
case clState cleaner of
Off -> return cleaner
On -> runCleaner (turnsLeft - 1) cleaner
simulateOnGrid :: Int -> Grid -> StdGen -> (Cleaner, Grid)
simulateOnGrid maxTurns grid gen =
evalState (runStateT (runCleaner maxTurns cleaner) grid) gen
where cleaner = createCleaner (fromJust $ cell (0,0) grid) East
我从GHCi中调用simulateOnGrid
函数,如下所示:
> gen <- newStdGen
> let grid = evalState (makeGrid (8,15) (8,15) 0.05) gen
> simulateOnGrid 5 grid gen
并且代码卡在了一行:
let (cleaner, grid) = runState (doAction GoForward cleaner) grid
我通过在代码中添加跟踪来确认。对doAction
函数的调用永远不会发生。
问题似乎是在runState
函数中使用runCleaner
,但我无法找到任何理由。
请解释原因以及是否有办法解决此问题。
另外,在monadic函数中使用runState
对我来说感觉不对。请建议是否有更好的方法。
答案 0 :(得分:6)
在let
绑定的右侧,绑定的名称在范围内,因此在您编写时
let (cleaner, grid) = runState (doAction GoForward cleaner) grid
cleaner
右侧的grid
和=
与左侧的let (cleaner', grid') = runState (doAction GoForward cleaner) grid
和runState
相同。这可能会导致无限循环,因为您将操作的输出反馈为其输入!为避免这种情况,请为输出使用不同的名称。
doAction
除此之外,使用doAction :: Monad m => Action -> Cleaner -> StateT Grid m Cleaner
这样的话是完全正确的。如果您将doAction
的类型更改为
chooseAction
你没有提供这个功能的主体,但我猜它仍然可以使用这种不那么受约束的类型签名。
现在您不必再为手动获取和放置状态而烦恼,因为case
可以直接在您的monad中运行,cleaner <- case ph of
[] -> doAction GoForward cleaner
_ -> do action <- lift $ chooseAction (head ph)
doAction action cleaner
可以通过首先解除它来运行。使用它,您的{{1}}表达式可以更简洁地编写:
{{1}}