Упрощение некоторого кода на Haskell - PullRequest
3 голосов
/ 05 августа 2009

Итак, я работаю над минимаксной реализацией для игры, похожей на шашки, чтобы помочь мне лучше изучить Haskell. Функция, с которой у меня возникли проблемы, берет список состояний игры и генерирует список непосредственных преемников игры. Как и в шашках, если прыжок доступен, игрок должен его выполнить. Если их больше одного, игрок может выбрать.

По большей части это хорошо работает с монадой списка: цикл по всем входным состояниям игры, цикл по всем шарикам, которые можно перепрыгнуть, цикл по всем прыжкам этого мрамора. Эта монада списков в конце хорошо сглаживает все списки в простой список состояний.

Хитрость в том, что, если для данного игрового состояния не найдено никаких переходов, мне нужно вернуть текущее игровое состояние, а не пустой список. Приведенный ниже код - лучший способ сделать это, но мне он кажется ужасным. Любые предложения о том, как его почистить?

eHex :: Coord -> Coord -- Returns the coordinates immediately to the east on the board
nwHex :: Coord -> Coord -- Returns the coordinates immediately to the northwest on the board

generateJumpsIter :: [ZertzState] -> [ZertzState]
generateJumpsIter states = do
    ws <- states
    case children ws of
      [] -> return ws
      n@_ -> n
  where 
    children ws@(ZertzState s1 s2 b p) = do
      (c, color)  <- occupiedCoords ws
      (start, end) <- [(eHex, wHex), (wHex, eHex), (swHex, neHex),
                       (neHex, swHex), (nwHex, seHex), (seHex, nwHex)]
      if (hexOccupied b $ start c) && (hexOpen b $ end c)
        then case p of
          1 -> return $ ZertzState (scoreMarble s1 color) s2
                                   (jumpMarble (start c) c (end c) b) p
          (-1) -> return $ ZertzState s1 (scoreMarble s2 color)
                                      (jumpMarble (start c) c (end c) b) p
        else []

РЕДАКТИРОВАТЬ: Предоставить пример подписей типов для функций * Hex.

Ответы [ 2 ]

3 голосов
/ 05 августа 2009

Хитрость в том, что, если для данного игрового состояния не найдено никаких переходов, мне нужно вернуть текущее игровое состояние, а не пустой список.

Почему? Я написал минимакс несколько раз, и я не могу представить себе использование такой функции. Разве вам не лучше с функцией типа

nextStates :: [ZertzState] -> [Maybe [ZertzState]]

или

nextStates :: [ZertzState] -> [[ZertzState]]

Однако, если вы действительно хотите вернуть «либо список следующих состояний, либо, если этот список пуст, исходное состояние», тогда вам нужен тип

nextStates :: [ZertzState] -> [Either ZertzState [ZertzState]]

, который вы можете затем легко сгладить.

Что касается того, как реализовать, я рекомендую определить вспомогательную функцию типа

[ZertzState] -> [(ZertzState, [ZertzState])]

и чем вы можете сопоставить

(\(start, succs) -> if null succs then Left start else Right succs)

за результат, а также различные другие вещи.

Как сказал Фред Брукс (перефразируя), как только вы получите правильные типы, код практически сам себя пишет.

1 голос
/ 05 августа 2009

Не злоупотребляйте нотациями монад для списка, это так тяжело даром. Кроме того, вы можете использовать понимание списка таким же образом:

do x <- [1..3]
   y <- [2..5]      <=>  [ x + y | x <- [1..3], y <- [2..5] ]
   return x + y

теперь для «упрощения»

listOfHex :: [(Coord -> Coord,Coord -> Coord)]
listOfHex = [ (eHex, wHex), (wHex, eHex), (swHex, neHex)
            , (neHex, swHex), (nwHex, seHex), (seHex, nwHex)]

generateJumpsIter :: [ZertzState] -> [ZertzState]
generateJumpsIter states =
    [if null ws then ws else children ws | ws <- states]
  where -- I named it foo because I don t know what it do....
    foo True   1  = ZertzState (scoreMarble s1 color) s2
                               (jumpMarble (start c) c (end c) b) p
    foo True (-1) = ZertzState s1 (scoreMarble s2 color)
                               (jumpMarble (start c) c (end c) b) p
    foo False  _  = []
    foo _ _ = error "Bleh"

    children ws@(ZertzState s1 s2 b p) =
      [ foo (valid c hex) p | (c, _)  <- occupiedCoords ws, hex <- listOfHex ]
        where valid c (start, end) =
                 (hexOccupied b $ start c) && (hexOpen b $ end c)

Понимание пустоты в верхнем списке немного беспокоит меня, но, поскольку у меня нет всего кода, я действительно не знаю, как это сделать другим способом. Если вы можете изменить более подробно, я предлагаю вам использовать больше комбинаторов (map, foldr, foldl 'и т. Д.), Поскольку они действительно уменьшают размер кода в моем опыте.

Обратите внимание, что код не проверен и может не скомпилироваться.

...