Это в основном теоретический вопрос о FP, но я возьму текстовые приключения (например, Zork старой школы), чтобы проиллюстрировать мою точку зрения. Мне бы хотелось узнать ваше мнение о том, как бы вы смоделировали симуляцию с состоянием с помощью FP.
Текстовые приключения действительно требуют ООП. Например, все «комнаты» являются экземплярами Room
класса, у вас может быть базовый Item
класс и интерфейсы, например, Item<Pickable>
для вещей, которые вы можете нести, и так далее.
Моделирование мира в FP работает по-другому, особенно если вы хотите обеспечить неизменность в мире, который должен изменяться по ходу игры (объекты перемещаются, враги побеждены, выигрыш растет, игрок меняет свое местоположение). Я представляю один большой объект, в World
котором есть все: какие комнаты вы можете исследовать, как они связаны, что несет игрок, какие рычаги были задействованы.
Я думаю, что чистый подход заключается в том, чтобы передать этот большой объект любой функции и вернуть его (возможно, модифицировать). Например, у меня есть moveToRoom
функция, которая получает World
и возвращает ее с World.player.location
измененной в новую комнату World.rooms[new_room].visited = True
и так далее.
Даже если это более «правильный» путь, похоже, он принуждает нас к чистоте. В зависимости от языка программирования передача этого потенциально очень большого World
объекта назад и вперед может быть дорогой. Кроме того, каждая функция может иметь доступ к любому World
объекту. Например, комната может быть доступной или нет в зависимости от рычага, сработавшего в другой комнате, потому что она может быть затоплена, но если игрок носит спасательный жилет, он может войти в него в любом случае. Монстр может быть агрессивным или нет в зависимости от того, убил ли игрок своего двоюродного брата в другой комнате. Это означает , что roomCanBeEntered
функция требует доступа World.player.invetory
и World.rooms
, describeMonster
необходимо получить доступ World.monsters
и так далее ( в основном, вы должныпередать всю нагрузку вокруг). Мне действительно кажется, что это требует глобальной переменной, даже если это почти хороший стиль программирования, особенно в FP.
Как бы вы решили эту проблему?
источник
Ответы:
Имейте в виду, что функциональные языки используют структуры данных и отдельные функции вместо объектов. Например, вместо этого у вас будет набор комнат и список предметов инвентаря.
В идеале вы также должны ограничивать объем данных, которые вы предоставляете функциям, до того, сколько они на самом деле требуют, насколько это возможно, вместо того, чтобы пройти весь мир (скажем, вы извлекаете одну релевантную комнату из своего мира; конечно, трудно полностью взаимозависимые миры). отдельно). Результат будет реинкорпирован в мировую структуру данных, создавая новое состояние. Вы не можете моделировать состояние без использования состояния; как вы говорите, некоторые вещи по своей природе требуют мутации.
Большинство практических функциональных языков предоставляют способ реализовать мутацию либо напрямую (скажем, монаду ST в Haskell или переходные процессы в Clojure), либо эффективно имитировать ее (часто путем повторного использования неизмененных частей структуры данных (хорошим примером здесь являются структуры данных Clojure по умолчанию)) ). В любом случае поддерживается чистота.
Поскольку количество состояний, которые должны быть изменены, кажется ограниченным, я бы не стал слишком беспокоиться о проблемах производительности и остановился на (возможно, уже оптимизированном) наивном функциональном подходе.
Другой вариант, который я видел, будет возвращать только инструкции, чтобы изменить какую-то часть мира от ваших индивидуальных функций, а затем обновлять ваш мир в соответствии с ними. Ряд сообщений в блоге, описывающих это, доступен по адресу http://prog21.dadgum.com/23.html .
Оба эти ответа больше касаются того, как организовать изменения, чем не передавать весь ваш мир функциям, потому что совершенно взаимозависимый не может быть сегментирован по определению - но попытайтесь сделать это как можно лучше в вашем случае, что не только функциональный, но и хорошая практика.
источник
Сам я бы определенно изучил способность рассматриваемого языка получить доступ к какой-либо форме базы данных. Большинство событий, которые одновременно изменяют состояние мира, просто записываются на диск и не влияют на текущего игрока в текущей комнате (за исключением особых обстоятельств, таких как взрывы или в MMO, переключатели, открывающие двери удаленно, крики других игроков и т. д.).
Таким образом, текущий клиент действительно должен знать только об
Room
объекте и вещах, которые непосредственно на него влияют.noticableEventsOutsideRoom
затем может быть просто подклассомRoom
затронутых недавними изменениями в базе данных, и ваш игровой объект стал намного меньше.источник
update mobs set agro=1 where distance<5
и был покончено с этим. Может быть, это не лучшая практика, но она подходит для моих целей. Что касается поиска пути через базу данных, всегда можно использовать алгоритм кратчайшего пути Дейкстры ...Настоящее решение не в том , чтобы собрать все в большой объект Мира, а затем передать его. Вместо этого рекомендуется точно указать тип функции, с которой вы имеете дело. Вот несколько примеров:
Плохой пример - попытка изменить существующий объект, но хороший пример - попытка создать мир из пространства состояний вашей игры. По сути, для создания мира вам необходимо знать все данные, необходимые для выбора правильного мира.
источник