Функціональне програмування та текстові пригоди


14

Це здебільшого теоретичне питання про ПП, але я буду брати текстові пригоди (на зразок старого шкільного Зорка), щоб проілюструвати свою думку. Мені хотілося б дізнатися ваші думки щодо того, як би ви змогли змоделювати модерацію з FP.

Текстові пригоди справді, схоже, закликають OOP. Наприклад, всі "номери" - це екземпляри 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.

Як би ви вирішили цю проблему?


4
"Залежно від мови програмування, передача цього потенційно дуже великого світового об'єкта назад і назад може бути дорогим." Це, ймовірно, буде передано шляхом посилання. "Також кожна функція може мати доступ до будь-якого об'єкта World." Мені важко повірити, що кожна функція потребує доступу до всього стану гри.
Довал

2
Я думаю, що дослідження Кріса Мартена було б цікавим, воно має на меті показати, як зробити інтерактивну художню літературу на декларативних мовах приємною. github.com/chrisamaphone/interactive-lp
Даніель

2
Ви можете ознайомитись із цим блогом, де описується авторський підхід до програмування такої гри функціонально. Зокрема, ця публікація є досить актуальною.
галас

3
Мені цікаво, чи вплинуло це питання на пізніше рішення @ EricLippert написати ряд статей про впровадження (частин) машини Z в Ocaml ...?
Jules

1
@Jules Посилання на початок цієї серії, для тих, хто цікавиться: ericlippert.com/2016/02/01/west-of-house
KChaloux

Відповіді:


4

Майте на увазі, що функціональні мови використовують структури даних та окремі функції замість об'єктів. Наприклад, ви мали б набір кімнат та список предметів інвентарю як світ.

В ідеалі ви також обмежите кількість даних, які ви надаєте функціям, настільки, наскільки вони насправді вимагають якомога більше замість того, щоб пройти весь світ (скажімо, ви вилучаєте єдину відповідну кімнату зі свого світу; звичайно, повністю взаємозалежні світи можуть бути важкими окремо). Результат буде переобладнаний у світову структуру даних, створивши нову державу. Ви не можете моделювати стан без використання стану; як ви говорите, деякі речі по суті потребують мутації.

Більшість практичних функціональних мов надають спосіб реалізувати мутацію або безпосередньо (скажімо, монада ST в Haskell або перехідні в Clojure), або ефективно їх моделювати (часто шляхом повторного використання незмінних частин структури даних (типові дані даних Clojure - хороший приклад тут) ). У будь-якому випадку підтримується чистота.

Оскільки кількість стану, яке потрібно мутувати, здається обмеженим, я б не переймався над питаннями продуктивності та йшов із (можливо, вже оптимізованим) наївним функціональним підходом.

Інший варіант, який я бачив, - це повернення лише інструкцій щодо зміни деякої частини світу від ваших окремих функцій, а потім оновлення вашого світу відповідно до цих. Серія публікацій блогу, що описують це, доступна на веб-сайті http://prog21.dadgum.com/23.html .

Обидві ці відповіді стосуються того, як організувати зміни, ніж не передати весь світ функціям, оскільки ідеально взаємозалежний не може бути сегментований за визначенням - але спробуйте зробити це якнайкраще у вашому випадку, що не тільки функціональна, але також хороша практика.


0

Сам я, безумовно, розглядав би можливість мови, про яку йде мова, отримати доступ до якоїсь форми бази даних. Більшість подій, які одночасно змінюють стан світу, просто записуються на диск, і не впливають на поточного програвача в поточному приміщенні (за винятком особливих обставин, таких як вибухи, або в MMO, комутатори, що відкривають двері віддалено, крики інших гравців тощо).

Таким чином, поточному клієнту дійсно потрібно лише знати про Roomоб'єкт та речі, які безпосередньо впливають на нього. noticableEventsOutsideRoomто може бути просто підкласом, на який Roomвпливають останні зміни в базі даних, і ваш ігровий об’єкт став значно меншим.


Я розумію, що цей підхід не дуже допомагає розшукувати чи викликати локальні події (наприклад, агросередовища на сусідніх мобах), але я, як відомо, зловживав базами даних в минулому ... я, мабуть, просто надішлю дзвінок update mobs set agro=1 where distance<5і буду зроблено з ним. Можливо, це не найкраща практика, але це відповідає моїм цілям. Щодо пошуку маршрутів через базу даних, завжди можна використовувати алгоритм найкоротшого шляху
Діжкстри

0

Справжнє рішення полягає не в тому, щоб зібрати все на великий об'єкт Світу, а потім передати його навколо. Натомість рекомендується точно вказати тип функції, з якою ви маєте справу. Ось кілька прикладів:

BAD:
   f :: (World, Int) -> World

Good:
   f :: (Int,Int,Int,Int) -> World

Поганий приклад - це спроба змінити існуючий об’єкт, але хороший приклад - це спроба створити світ із простору стану, який має ваша гра. В основному для створення світу потрібно знати всі дані, необхідні для вибору правильного світу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.