3

私は、セルのグリッドを含み、グリッドをナビゲートするためにランダムな動きを選択する「AI-AModernApproach」本の問題2.8を解決しようとしています。

2.7 X mの長方形の部屋の環境を実装します。ここで、各正方形には5%の確率で汚れが含まれ、nとmは8から15までの範囲からランダムに選択されます。

2.8帰宅の要件を無視して、演習2.7の環境用の純粋な反射エージェントを設計および実装し、そのパフォーマンスを測定します。

そのため、2つの状態モナドを使用しました。1つGridは状態として、もう1つは状態として使用し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

simulateOnGrid私はこのようにGHCiから関数を呼び出します:

> 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、理由がわかりません。

その理由と、この問題を解決する方法があるかどうかを説明してください。

また、runStateモナディック関数の内部で使用することは私には間違っていると感じます。それを行うためのより良い方法があるかどうかを提案してください。

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これで、モナドで直接実行chooseActionでき、最初に状態を持ち上げて実行できるため、状態を手動で取得および配置する必要がなくなりました。これを使用すると、case式をより簡潔に書くことができます。

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