Хорошо, наша цель - поместить эту функцию в монаду Wrtier.
reaches :: KnightPos -> KnightPos -> Int -> Bool
reaches _ _ 0 = False
reaches from pos n =
any (\p -> p == pos || reaches p pos (n-1)) $ moveKnight from
Итак, начнем с подписи типа. Просто добавьте Writer
вокруг типа результата:
reaches :: KnightPos -> KnightPos -> Int -> Writer [String] Bool
Исходная функция не вернула [Bool]
, поэтому у новой функции нет причин возвращать Writer [String] [Bool]
. Поднимите возвращаемое значение базового случая:
reaches _ _ 0 = return False
Как вы и подозревали, рекурсивный случай становится немного сложнее. Давайте начнем, как вы, с tell
текущего pos
, который вы сделали правильно.
reaches from pos n = do
tell ["-->" ++ show pos]
moveKnight
нет в монаде писателя, поэтому нам не нужно связывать его, используя <-
для его вызова. Просто используйте let
(т. Е. Мы можем заменить moveKnight pos
всякий раз, когда мы используем нашу новую переменную, если мы хотим):
let moves = moveKnight from
Теперь давайте получим список рекурсивных результатов. На этот раз мы do должны связать, так как мы получаем Bool
из Writer [String] Bool
. Мы будем использовать монадический вариант map
, mapM :: (a -> m b) -> [a] -> m [b]
:
np <- mapM (\p -> reachesm p pos (n-1)) ps
Сейчас np :: [Bool]
. Итак, мы просто заканчиваем вашу логику:
return (any (pos ==) moves || or np)
or :: [Bool] -> Bool
это просто any id
.
Помните, чтобы связать переменную, когда вы хотите получить a
из m a
, используйте <-
, в противном случае используйте let
.
Для использования с main
вы можете использовать runWriter :: Writer w a -> (w,a)
:
main = print $ runWriter (reachesm (6,2) (6,1) 3)
В этом коде все еще есть ошибка, но он компилирует и выдает то, что вы сказали ему по каналу записи, поэтому должно быть достаточно, чтобы вы могли легко отладить оставшуюся проблему. Надеюсь, это помогло.