Dies ist größtenteils eine theoretische Frage zu FP, aber ich nehme Textabenteuer (wie Old-School-Zork), um meinen Standpunkt zu veranschaulichen. Ich würde gerne wissen, wie Sie eine Stateful-Simulation mit FP modellieren würden.
Text-Abenteuer scheinen wirklich OOP zu erfordern. Zum Beispiel sind alle "Räume" Instanzen einer Room
Klasse, Sie können eine Basisklasse Item
und Schnittstellen wie Item<Pickable>
für Dinge haben, die Sie tragen können und so weiter.
Die Weltmodellierung in FP funktioniert anders, insbesondere wenn Sie Unveränderlichkeit in einer Welt erzwingen möchten, die im Verlauf des Spiels mutieren muss (Objekte werden bewegt, Feinde werden besiegt, die Punktzahl wächst, der Spieler ändert seinen Standort). Ich stelle mir ein einziges großes Objekt vor World
, das alles bietet: Welche Räume können Sie erkunden, wie sind sie miteinander verbunden, was der Spieler trägt, welche Hebel wurden betätigt?
Ich denke, dass ein reiner Ansatz wäre, dieses große Objekt grundsätzlich an eine beliebige Funktion zu übergeben und es von ihnen zurückgeben zu lassen (möglicherweise modifiziert). Zum Beispiel habe ich eine moveToRoom
Funktion, die es bekommt World
und mit World.player.location
dem neuen Raum verändert zurückgibt , World.rooms[new_room].visited = True
und so weiter.
Auch wenn dies der "korrektere" Weg ist, scheint es die Reinheit deswegen zu erzwingen. Abhängig von der Programmiersprache World
kann das Hin- und Herbewegen dieses möglicherweise sehr großen Objekts teuer sein. Außerdem muss jede Funktion möglicherweise Zugriff auf ein beliebiges World
Objekt haben. Zum Beispiel kann ein Raum zugänglich sein oder nicht, abhängig von einem Hebel, der in einem anderen Raum ausgelöst wurde, weil er möglicherweise überflutet ist. Wenn der Spieler jedoch eine Schwimmweste trägt, kann er ihn trotzdem betreten. Ein Monster kann aggressiv sein oder nicht, je nachdem, ob der Spieler seinen Cousin in einem anderen Raum getötet hat. Dies bedeutet , dass die roomCanBeEntered
Funktion zugreifen muss World.player.invetory
und World.rooms
, describeMonster
Bedürfnisse Zugang World.monsters
und so weiter (im Grunde, Sie müssendie ganze Ladung herumreichen). Das scheint mir wirklich eine globale Variable zu sein, auch wenn dies alles andere als ein guter Programmierstil ist, besonders in FP.
Wie würden Sie dieses Problem lösen?
quelle
Antworten:
Beachten Sie, dass funktionale Sprachen Datenstrukturen und getrennte Funktionen anstelle von Objekten verwenden. Zum Beispiel hätten Sie stattdessen eine Reihe von Räumen und eine Liste von Inventargegenständen als Welt.
Idealerweise beschränken Sie die Datenmenge, die Sie Funktionen zur Verfügung stellen, auf die tatsächlich erforderliche Menge, anstatt die gesamte Welt zu durchlaufen (sagen wir, Sie extrahieren einen einzelnen relevanten Raum aus Ihrer Welt; es kann natürlich schwierig sein, vollständig voneinander abhängige Welten zu finden) trennen). Das Ergebnis würde wieder in die Datenstruktur der Welt eingehen und einen neuen Staat schaffen. Sie können den Zustand nicht modellieren, ohne den Zustand zu verwenden. Wie Sie sagen, erfordern einige Dinge von Natur aus eine Mutation.
Die meisten praktischen funktionalen Sprachen bieten die Möglichkeit, Mutationen entweder direkt (z. B. die ST-Monade in Haskell oder Transienten in Clojure) oder effizient zu simulieren (häufig durch Wiederverwendung unveränderter Teile der Datenstruktur). (Die Standarddatenstrukturen von Clojure sind hier ein gutes Beispiel.) ). So oder so bleibt die Reinheit erhalten.
Da das Ausmaß des Zustands, der mutiert werden muss, begrenzt zu sein scheint, würde ich mich nicht zu sehr um Leistungsprobleme kümmern und den (möglicherweise bereits optimierten) naiven funktionalen Ansatz wählen.
Eine andere Option, die ich gesehen habe, wäre, nur Anweisungen zurückzugeben, um einen Teil der Welt von Ihren individuellen Funktionen zu ändern, und dann Ihre Welt gemäß diesen zu aktualisieren. Eine Reihe von Blogposts, in denen dies beschrieben wird, finden Sie unter http://prog21.dadgum.com/23.html .
In beiden Antworten geht es mehr darum, Veränderungen zu organisieren, als Ihre gesamte Welt an Funktionen zu übergeben, da eine vollkommen voneinander abhängige nicht per Definition segmentiert werden kann - aber versuchen Sie dies so gut wie möglich in Ihrem Fall, was nicht nur der Fall ist funktional, aber auch gute Praxis.
quelle
Ich selbst würde auf jeden Fall die Fähigkeit der fraglichen Sprache untersuchen, auf irgendeine Form von Datenbank zuzugreifen. Die meisten Ereignisse, die gleichzeitig den Zustand der Welt verändern, werden einfach auf der Festplatte aufgezeichnet und wirken sich nicht auf den aktuellen Player im aktuellen Raum aus (außerhalb besonderer Umstände wie Explosionen oder in einem MMO, die Türen öffnen) aus der Ferne, Rufe anderer Spieler, etc).
Als solches muss der aktuelle Kunde sich des
Room
Objekts und der Dinge, die es direkt betreffen , nur bewusst sein .noticableEventsOutsideRoom
könnte dann einfach eine Unterklasse sein,Room
die von den letzten Änderungen an der Datenbank betroffen ist, und Ihr Spielobjekt ist viel kleiner geworden.quelle
update mobs set agro=1 where distance<5
und sein fertig damit. Vielleicht ist das nicht die beste Vorgehensweise, aber es entspricht meinen Zwecken. Bezüglich der Pfadfindung über eine Datenbank könnte man immer den Dijkstra-Algorithmus mit dem kürzesten Pfad verwenden ...Die wirkliche Lösung besteht nicht darin, alles auf einem großen Weltobjekt zu sammeln und es dann weiterzugeben. Stattdessen wird empfohlen, den Typ der Funktion, mit der Sie sich befassen, genau anzugeben. Hier sind einige Beispiele:
Das schlechte Beispiel versucht, vorhandene Objekte zu modifizieren, aber das gute Beispiel versucht, aus dem Zustandsraum, den Ihr Spiel hat, eine Welt zu erschaffen. Grundsätzlich müssen Sie zum Erstellen einer Welt alle Daten kennen, die zum Auswählen der richtigen Welt erforderlich sind.
quelle