Я изучаю функциональное программирование на Haskell . В то же время я изучаю теорию автоматов и, поскольку они, кажется, хорошо сочетаются друг с другом, я пишу небольшую библиотеку для игры с автоматами.
Вот проблема, которая заставила меня задать вопрос. Изучая способ оценки достижимости состояний, я пришел к выводу, что простой рекурсивный алгоритм будет весьма неэффективным, поскольку некоторые пути могут совместно использовать некоторые состояния, и я мог бы в конечном итоге оценить их несколько раз.
Например, здесь, оценка достижимости из г от , я должен был бы исключить е и при проверке пути через D и C :
Таким образом, моя идея состоит в том, что алгоритм, работающий параллельно по многим путям и обновляющий общую запись исключенных состояний, может быть хорошим, но это слишком много для меня.
Я видел, что в некоторых простых случаях рекурсии можно передать состояние в качестве аргумента, и это то, что я должен сделать здесь, потому что я передаю список состояний, через которые я прошел, чтобы избежать циклов. Но есть ли способ передать этот список и в обратном направлении, например, вернуть его в кортеже вместе с логическим результатом моей canReach
функции? (хотя это кажется немного вынужденным)
Помимо обоснованности моего примера , какие еще методы доступны для решения такого рода проблем? Я чувствую, что они должны быть достаточно распространенными, чтобы были такие решения, как то, что происходит с fold*
или map
.
До сих пор, читая learnyouahaskell.com, я не нашел ни одного, но учтите , что еще не трогал монады.
( если интересно, я разместил свой код на codereview )
источник
Ответы:
Функциональное программирование не избавляет от состояния. Это только делает это явным! Хотя это правда, что такие функции, как map, часто «распутывают» «разделяемую» структуру данных, если все, что вы хотите сделать, это написать алгоритм достижимости, то это просто вопрос отслеживания того, какие узлы вы уже посетили:
Теперь я должен признаться, что отслеживание всего этого состояния вручную довольно раздражает и подвержено ошибкам (легко использовать s 'вместо s' ', легко передать один и тот же s более чем в одно вычисление ...) , Вот где приходят монады: они не добавляют ничего, что вы не могли сделать раньше, но позволяют неявно передавать переменную состояния, а интерфейс гарантирует, что это происходит однопоточным способом.
Редактировать: я попытаюсь дать более подробное обоснование того, что я сделал сейчас: во-первых, вместо того, чтобы просто проверять достижимость, я кодировал поиск в глубину. Реализация будет выглядеть примерно так же, но отладка выглядит немного лучше.
На языке с сохранением состояния DFS будет выглядеть примерно так:
Теперь нам нужно найти способ избавиться от изменчивого состояния. Прежде всего мы избавляемся от переменной "listlist", заставляя dfs возвращать это вместо void:
А теперь самое сложное: избавиться от «посещаемой» переменной. Основная хитрость заключается в использовании соглашения, в котором мы передаем состояние в качестве дополнительного параметра функциям, которые в нем нуждаются, и эти функции возвращают новую версию состояния в качестве дополнительного возвращаемого значения, если они хотят его изменить.
Чтобы применить этот шаблон к dfs, нам нужно изменить его, чтобы получать набор «посещенных» в качестве дополнительного параметра и возвращать обновленную версию «посещенных» в качестве дополнительного возвращаемого значения. Кроме того, нам нужно переписать код, чтобы мы всегда передавали «самую последнюю» версию «посещенного» массива:
Версия на Haskell в значительной степени делает то, что я сделал здесь, за исключением того, что она идет до конца и использует внутреннюю рекурсивную функцию вместо изменяемых переменных curr_visited и childtrees.
Что касается монад, то, что они в основном выполняют, - это неявная передача «curr_visited» вместо того, чтобы заставлять вас делать это вручную. Это не только удаляет помехи из кода, но также предотвращает ошибки, такие как разветвление состояния (передача одного и того же «посещенного» набора в два последующих вызова вместо цепочки состояния).
источник
Вот простой ответ, на который можно положиться
mapConcat
.Куда
neighbors
возвращает состояния, непосредственно связанные с государством. Это возвращает серию путей.источник