状态内的runState Monadic函数不起作用

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函数里面使用对我来说感觉不对.请建议是否有更好的方法.

ham*_*mar 6

let绑定的右侧,绑定的名称在范围内,因此在您编写时

let (cleaner, grid) = runState (doAction GoForward cleaner) grid
Run Code Online (Sandbox Code Playgroud)

cleanergrid上的右手边=是相同的人上左侧的人.这可能会导致无限循环,因为您将操作的输出反馈为其输入!为避免这种情况,请为输出使用不同的名称.

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)