Может оказаться полезным немного отойти от концепции Monad, подумав об этом коде, если вы обнаружите, что простые старые операции со списками более естественны для вас. Таким образом, вы можете переписать пример кода (с небольшой очисткой для удобочитаемости) как:
type KnightPos = (Int,Int)
moveKnight :: KnightPos -> [KnightPos]
moveKnight (c,r) = filter legal candidates where
candidates = [(c+2,r-1), (c+2,r+1), (c-2,r-1), (c-2,r+1),
(c+1,r-2), (c+1,r+2), (c-1,r-2), (c-1,r+2)]
legal (c',r') = c' `elem` [1..8] && r' `elem` [1..8]
in3 :: KnightPos -> [KnightPos]
in3 start = concatMap moveKnight (concatMap moveKnight (moveKnight start))
canReachIn3 :: KnightPos -> KnightPos -> Bool
canReachIn3 start end = end `elem` in3 start
Секретный соус в concatMap
. Как вы, наверное, уже знаете, это синоним >>=
в монаде List
, но сейчас может быть более полезным думать о ней как о сопоставлении функции типа KnightPos -> [KnightPos]
со списком [KnightPos]
для получения списка списков [[KnightPos]]
, а затем свести результат обратно в один список.
Хорошо, теперь, когда мы на данный момент обошлись без монад, давайте вернемся к загадке ... Допустим, ваш начальный KnightPos
равен (4,4)
, и вы хотите отслеживать все возможные последовательности ходов из эта позиция. Итак, определите синоним другого типа:
type Sequence = [KnightPos]
Тогда вам нужно, чтобы moveKnight
работал с этими последовательностями, находя все возможные ходы из последней позиции в последовательности:
moveKnight :: Sequence -> [Sequence]
Итак, начиная с последовательности [(4,4)]
, мы получили бы список последовательностей: [[(4,4), (6,3)], [(4,4), (6,5)], [(4,4), (2,3)], ... ]
. Тогда я думаю, что единственное изменение, которое вам нужно внести в in3
, это соответственно исправить сигнатуру типа:
in3 :: Sequence -> [Sequence]
Я не думаю, что реальная реализация изменится. Наконец, вам нужно, чтобы canReachIn3
выглядел примерно так:
canReachIn3 :: KnightPos -> KnightPos -> [Sequence]
Я намеренно оставляю здесь детали реализации, поскольку не хочу полностью разрушать для вас загадку, но надеюсь, что проиллюстрировал здесь то, что нет ничего особенно «особенного» о списке, или список списков, или что-то еще. Все, что мы действительно сделали, это заменили функцию типа KnightPos -> [KnightPos]
новой функцией типа Sequence -> [Sequence]
- практически такой же формы. Поэтому заполните реализации каждой функции, используя любой стиль, который кажется вам естественным, а затем, как только он заработает, вернитесь к монадическому стилю, заменив concatMap
на >>=
и т. Д.