Programación funcional y aventuras de texto

7

Esta es principalmente una pregunta teórica sobre la FP, pero tomaré aventuras de texto (como la vieja escuela Zork) para ilustrar mi punto. Me gustaría saber sus opiniones sobre cómo modelaría una simulación con estado con FP.

Las aventuras de texto realmente parecen requerir OOP. Por ejemplo, todas las "habitaciones" son instancias de una clase Room , puede tener una clase básica Item e interfaces como Item<Pickable> para las cosas que puede transportar, etc.

El modelado mundial en FP funciona de manera diferente, especialmente si quieres imponer la inmutabilidad en un mundo que debe mutar a medida que avanza el juego (los objetos se mueven, los enemigos son derrotados, la puntuación aumenta, el jugador cambia su localización). Imagino un solo objeto grande World que lo tiene todo: qué salas se pueden explorar, cómo están vinculadas, qué lleva el jugador, qué palancas se han activado.

Creo que un enfoque puro sería, básicamente, pasar este objeto grande a cualquier función y que sea devuelto por ellos (posiblemente modificado). Por ejemplo, tengo una función moveToRoom que obtiene World y la devuelve con World.player.location cambiado a la nueva sala, World.rooms[new_room].visited = True y así sucesivamente.

Incluso si esta es la forma más "correcta", parece estar imponiendo la pureza por el bien de ella. Dependiendo del lenguaje de programación, pasar este objeto World potencialmente muy grande de un lado a otro puede ser costoso. Además, es posible que todas las funciones deban tener acceso a cualquier objeto World . Por ejemplo, una habitación puede ser accesible o no dependiendo de una palanca activada en otra habitación porque puede estar inundada, pero si el jugador lleva un chaleco salvavidas, puede entrar en ella de todos modos. Un monstruo puede ser agresivo o no dependiendo de si el jugador ha matado a su primo en otra habitación. Esto significa que la función roomCanBeEntered necesita acceder a World.player.invetory y World.rooms , describeMonster necesita acceder a World.monsters y así sucesivamente (básicamente, debe pasar la carga completa). Realmente me parece que esto requiere una variable global, incluso si se trata de un buen estilo de programación, especialmente en FP.

¿Cómo resolverías este problema?

    
pregunta pistacchio 08.05.2015 - 19:55

3 respuestas

2

Tenga en cuenta que los lenguajes funcionales utilizan estructuras de datos y funciones separadas en lugar de objetos. Por ejemplo, tendría un conjunto de habitaciones y un inventario como un mundo en su lugar.

Idealmente, también limitarías la cantidad de datos que das a las funciones a cuánto realmente requieren tanto como sea posible en lugar de pasar al mundo entero (digamos que extraes la sala D de tu mundo; por supuesto, los mundos completamente interdependientes pueden ser difíciles separar). El resultado se reincorporará a la estructura de datos del mundo, creando un nuevo estado. No puedes modelar el estado sin usar el estado; Como dices, algunas cosas inherentemente requieren mutación.

La mayoría de los lenguajes funcionales prácticos proporcionan una forma de realizar la mutación directamente (por ejemplo, la mónada IO en Haskell o transients en Clojure), o simularla de manera eficiente (a menudo reutilizando partes sin cambios de la estructura de datos (las estructuras de datos predeterminadas de Clojure son una buena opción). ejemplo aquí)). De cualquier manera se mantiene la pureza.

Dado que la cantidad de estado que necesita ser mutada parece limitada, no me preocuparía demasiado por los problemas de rendimiento e iría con el enfoque funcional ingenuo (posiblemente ya optimizado).

Una opción diferente que he visto sería devolver solo las instrucciones para cambiar alguna parte del mundo de sus funciones individuales y luego actualizar su mundo de acuerdo con estas. Una serie de publicaciones de blog que describen esto está disponible en enlace .

Ambas respuestas tratan más sobre cómo organizar los cambios que no pasar todo el mundo a las funciones, porque uno perfectamente interdependiente no puede segmentarse por definición , pero intente hacerlo lo mejor posible En su caso, puede hacerlo, que no solo es funcional, sino que también es una buena práctica.

    
respondido por el hyperfekt 28.05.2015 - 19:21
0

Yo mismo, definitivamente, analizaría la capacidad del lenguaje en cuestión para acceder a algún tipo de base de datos. La mayoría de los eventos que suceden para cambiar el estado del mundo simultáneamente se grabarán en el disco y no afectarán al jugador actual dentro de la sala actual (fuera de circunstancias especiales como explosiones o en un MMO, interruptores que abren las puertas). remotamente, los gritos de otros jugadores, etc.).

Como tal, el cliente actual solo necesita conocer el objeto Room y las cosas que lo afectan directamente. noticableEventsOutsideRoom podría ser simplemente una subclase de Room afectada por los cambios recientes en la base de datos, y su objeto de juego se ha vuelto mucho más pequeño.

    
respondido por el Ayelis 15.05.2015 - 02:42
0

La solución real es no recolectando todo en un gran objeto mundial y luego pasándolo. En su lugar, se recomienda que especifique con precisión el tipo de función con la que está tratando. Aquí hay algunos ejemplos:

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

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

El mal ejemplo es tratar de modificar un objeto existente, pero el buen ejemplo es tratar de crear un mundo desde el espacio de estado que tiene tu juego. Básicamente, para crear un mundo, necesitas saber todos los datos necesarios para seleccionar el mundo correcto.

    
respondido por el tp1 27.06.2015 - 21:31

Lea otras preguntas en las etiquetas