#haskell #state-monad
#haskell #состояние-монада
Вопрос:
Если у нас есть доска 4×4 с символами (которые могут повторяться) и некоторым случайным словом, нам нужно найти, можно ли найти это слово на доске. Теперь эту проблему довольно легко решить, не используя никаких навороченных штучек. Но с точки зрения обучения, как можно решить эту проблему, когда нам нужно учитывать эти вещи:
- плата постоянна и не собирается меняться, поэтому, допустим, мы заинтересованы в использовании Reader для нее
- при поиске слова нам нужно сохранить набор посещенных плиток на доске, поэтому, допустим, мы заинтересованы в использовании State для этого
Первой идеей было использовать что-то вроде этого:
findWord :: String -> Position -> ReaderT Board (State (S.Set Position)) -> Bool
входная строка — это оставшаяся часть слова, которое мы ищем
где Position (Int, Int) используется для определения того, где мы в данный момент находимся на доске (и устанавливает хранилища, какие позиции мы посетили во время этого пути поиска)
Плата просто хранит информацию о символах в каждой позиции, для простоты ее можно рассматривать как [[Char]]
возвращаемый Bool предназначен для использования в качестве конечного результата, чтобы сказать, содержит ли доска слово или нет
поиск на доске может выполняться во всех направлениях (поэтому диагонали включены); слово не обязательно должно располагаться ни на горизонтальной, ни на диагональной линии — пока символы слова соединены на доске — решение допустимо.
Теперь о реальной проблеме: если мы используем State для решения этой проблемы, как нам гарантировать, что функция вернется, как только слово найдено, и не продолжит поиск других путей? Например, если слово, которое мы ищем, «AAAAA», а доска — это просто 16 символов «A», то функция должна вернуться примерно через 5 итераций и не продолжить поиск по всем возможным путям (потому что количество успешных путей огромно)?
Я ищу несколько советов о том, как подойти к этой проблеме, используя одно ограничение, которое является обязательным: использование State monad. Ценна любая информация, будь то совет, псевдокод или даже рабочее решение. Спасибо.
Ответ №1:
Вместо возврата Bool
могу ли я предложить использовать другую монаду в стеке transformer с явной возможностью сокращать вычисления, например Maybe
:
findWord :: String -> Position -> ReaderT Board (StateT (S.Set Position) Maybe) ()
Я думаю, что это правильная точка в стеке, чтобы автоматически возвращать состояние после каждого сбоя.
Теперь вы можете использовать msum
(из MonadPlus
класса), который будет проверять каждый параметр в списке по очереди и (для стека на Maybe
основе) останавливать первый подоперационный, который завершается успешно, или завершать весь, msum
если ни один из них не выполняется. Например, что-то вроде
msum $ map (findWord remainingString) listOfNeighboringPositions
Комментарии:
1. Большое вам спасибо, msum сработал великолепно. Узнал кое-что новое. Все заработало. Еще раз, спасибо!