3

I am trying to solve the problem 2.8 of "AI - A Modern Approach" book which involves a grid of cells and choosing random moves to navigate the grid.

2.7 Implement an environment for a n X m rectangular room, where each square has a 5% chance of containing dirt, and n and m are chosen at random from the range 8 to 15, inclusive.

2.8 Design and implement a pure reflex agent for the environment of Exercise 2.7, ignoring the requirement of returning home, and measure its performance.

So I have used two state monads - one with Grid as the state and another with StdGen as the state. The code compile without any error but when I run it from GHCi, it gets stuck and does not return.

The relevant part of the code:

Supporting code

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

Main code

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

I invoke the simulateOnGrid function from GHCi like this:

> gen <- newStdGen
> let grid = evalState (makeGrid (8,15) (8,15) 0.05) gen
> simulateOnGrid 5 grid gen

and code gets stuck at the line:

let (cleaner, grid) = runState (doAction GoForward cleaner) grid

which I have confirmed by putting traces in the code. The call to the doAction function never happens.

The issue seems to be the use of runState inside the runCleaner function, but I am unable to find any reason for it.

Please explain the reason and if there is a way to solve this issue.

Also, using runState inside the a monadic function feels wrong to me. Please suggest if there is a better way to do it.

4

1 回答 1

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可以直接在您的 monad 中运行,并且chooseAction可以通过先解除它来运行。使用它,您的case表达式可以写得更简洁:

cleaner <- case ph of
    [] -> doAction GoForward cleaner
    _  -> do action <- lift $ chooseAction (head ph)
             doAction action cleaner
于 2011-10-04T19:27:52.347 回答