Abh*_*kar 3 random haskell state-monad
我试图解决"AI - 现代方法"一书中的问题2.8,该书涉及一组单元格并选择随机移动来导航网格.
2.7为X m矩形房间实施环境,每个方格有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
Run Code Online (Sandbox Code Playgroud)
主要代码
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
Run Code Online (Sandbox Code Playgroud)
我simulateOnGrid从GHCi 调用这个函数:
> gen <- newStdGen
> let grid = evalState (makeGrid (8,15) (8,15) 0.05) gen
> simulateOnGrid 5 grid gen
Run Code Online (Sandbox Code Playgroud)
和代码卡在行:
let (cleaner, grid) = runState (doAction GoForward cleaner) grid
Run Code Online (Sandbox Code Playgroud)
我通过在代码中添加痕迹来确认.对doAction函数的调用永远不会发生.
问题似乎是在函数runState内部使用runCleaner,但我无法找到任何理由.
请解释原因以及是否有办法解决此问题.
另外,runState在monadic函数里面使用对我来说感觉不对.请建议是否有更好的方法.
在let绑定的右侧,绑定的名称在范围内,因此在您编写时
let (cleaner, grid) = runState (doAction GoForward cleaner) grid
Run Code Online (Sandbox Code Playgroud)
在cleaner和grid上的右手边=是相同的人上左侧的人.这可能会导致无限循环,因为您将操作的输出反馈为其输入!为避免这种情况,请为输出使用不同的名称.
let (cleaner', grid') = runState (doAction GoForward cleaner) grid
Run Code Online (Sandbox Code Playgroud)
除此之外,你是绝对正确的,这样使用runState是奇怪的.我认为,如果你改变的类型,你可以大大简化事情doAction来
doAction :: Monad m => Action -> Cleaner -> StateT Grid m Cleaner
Run Code Online (Sandbox Code Playgroud)
你没有提供这个功能的主体,但我猜它仍然适用于这种不太受约束的类型签名.
现在你不必再为手动获取和放置状态,因为doAction可以直接在你的monad chooseAction中运行,并且可以通过先提升它来运行.使用它,您的case表达式可以更简洁地编写:
cleaner <- case ph of
[] -> doAction GoForward cleaner
_ -> do action <- lift $ chooseAction (head ph)
doAction action cleaner
Run Code Online (Sandbox Code Playgroud)