Ich habe vor kurzem implementiert für Spaß Conways Spiel des Lebens in Javascript (tatsächlich aber Coffee gleiche Sache). Da Javascript als funktionale Sprache verwendet werden kann, habe ich versucht, an diesem Ende des Spektrums zu bleiben. Ich war mit meinen Ergebnissen nicht zufrieden. Ich bin ein ziemlich guter OO-Programmierer, und meine Lösung steckt voller Gleichaltriger. So lange Frage kurz: Was ist der (Pseudocode-) Funktionsstil dafür?
Hier ist Pseudocode für meinen Versuch:
class Node
update: (board) ->
get number_of_alive_neighbors from board
get this_is_alive from board
if this_is_alive and number_of_alive_neighbors < 2 then die
if this_is_alive and number_of_alive_neighbors > 3 then die
if not this_is_alive and number_of_alive_neighbors == 3 then alive
class NodeLocations
at: (x, y) -> return node value at x,y
of: (node) -> return x,y of node
class Board
getNeighbors: (node) ->
use node_locations to check 8 neighbors
around node and return count
nodes = for 1..100 new Node
state = new NodeState(nodes)
locations = new NodeLocations(nodes)
board = new Board(locations, state)
executeRound:
state = clone state
accumulated_changes = for n in nodes n.update(board)
apply accumulated_changes to state
board = new Board(locations, state)
functional-programming
George Mauer
quelle
quelle
Antworten:
Nun, ein paar Ideen. Ich bin kein Experte in FP, aber ...
Es ist ziemlich klar, dass wir einen Typ haben sollten,
Board
der einen Spielstatus darstellt. Die Basis der Implementierung sollte eineevolve
Funktion vom Typ seinevolve :: Board -> Board
; Das heißt, es entsteht einBoard
aus der Anwendung der Spielregeln auf einBoard
.Wie sollen wir umsetzen
evolve
? ABoard
sollte wahrscheinlich eine nxm-Matrix vonCell
s sein. Wir könnten eine FunktioncellEvolve
vom Typ implementieren,cellEvolve :: Cell -> [Cell] -> Cell
die mit aCell
und seinen NachbarnCell
denCell
Zustand in der nächsten Iteration berechnet .Wir sollten auch eine
getCellNeighbors
Funktion implementieren , die aCell
s Nachbarn aus a extrahiertBoard
. Ich bin mir der Signatur dieser Methode nicht ganz sicher. Abhängig davon, wie Sie implementierenCell
und wasBoard
Sie beispielsweise haben könntengetCellNeighbors :: Board -> CoordElem -> CoordElem -> [Cell]
, wenn Sie ein Board und zwei Koordinaten (CoordElem
der Typ, der zum Indizieren von Positionen in a verwendet wirdBoard
) angeben , erhalten Sie eine Liste mit variabler Länge der Nachbarn (nicht alle Zellen im Board haben die gleiche Anzahl von Nachbarn - Ecken haben 3 Nachbarn, Grenzen 5 und alle anderen, 8).evolve
kann also durch KombinierencellEvolve
undgetCellNeighbors
für alle Zellen in der Karte implementiert werden. Die genaue Implementierung hängt wiederum davon ab, wie SieBoard
und implementieren.Cell
Es sollte jedoch so etwas wie "für alle Zellen in der aktuellen Karte" sein, die Nachbarn abrufen und zur Berechnung der neue Zelle der Karte ". Dies sollte mit einer generischen Anwendung dieser Funktionen über die gesamte Karte unter Verwendung einer" Karte über die Zellenfunktion der Karte "möglich sein.Andere Gedanken:
Sie sollten wirklich
cellEvolve
so implementieren , dass als Parameter ein Typ verwendet wird,GameRules
der die Regeln des Spiels codiert - beispielsweise eine Liste von Tupeln,(State,[(State,NumberOfNeighbors)],State)
die für einen bestimmten Zustand und die Anzahl der Nachbarn in jedem Zustand besagt, der Zustand in der nächsten Iteration sein sollte .cellEvolve
könnte dann die signatur seincellEvolve :: GameRules -> Cell -> [Cell] -> Cell
Dies würde dich logischerweise dazu bringen, dich in zu
evolve :: Board -> Board
verwandelnevolve :: GameRules -> Board -> Board
, so dass du esevolve
unverändert mit anderen verwendenGameRules
könntest, aber du könntest einen Schritt weiter gehen undcellEvolve
stattdessen pluggable machenGameRules
.Das Spielen mit
getCellNeighbors
Ihnen könnte auchevolve
generisch in Bezug auf dieBoard
TopologiegetCellNeighbors
der Karte sein - Sie könnten die Kanten der Karte, die 3D-Karte usw. umschließen.quelle
Wenn Sie eine funktionale Programmierversion von Life schreiben, sind Sie es sich selbst schuldig, etwas über Gospers Algorithmus zu lernen. Es nutzt Ideen aus der funktionalen Programmierung, um dies zu erreichen Billionen von Generationen pro Sekunde auf Brettern mit Billionen von Quadraten auf einer Seite . Das hört sich unmöglich an, ich weiß, aber es ist durchaus möglich; Ich habe eine nette kleine Implementierung in C #, die quadratische Bretter 2 ^ 64 Quadrate auf einer Seite leicht handhabt.
Der Trick besteht darin, die massive Selbstähnlichkeit von Life-Boards sowohl zeitlich als auch räumlich auszunutzen. Indem Sie sich den zukünftigen Zustand großer Teile der Platine merken, können Sie große Teile auf einmal schnell vorrücken.
Ich wollte schon seit vielen Jahren eine Einführung in Gospers Algorithmus veröffentlichen, aber ich hatte nie die Zeit dafür. Wenn ich das mache, poste ich hier einen Link.
Beachten Sie, dass Sie Gospers Algorithmus für Lebensberechnungen nachschlagen möchten , nicht den Gospers Algorithmus für die Berechnung hypergeometrischer Summen.
quelle
Zufälligerweise haben wir dieses Problem heute in unserem Haskell-Vortrag behandelt. Zum ersten Mal habe ich es gesehen, aber hier ist ein Link zum Quellcode, den wir erhalten haben:
http://pastebin.com/K3DCyKj3
quelle
Vielleicht möchten Sie sich von den Implementierungen in RosettaCode inspirieren lassen.
Beispielsweise gibt es funktionale Haskell- und OCaml-Versionen, die jede Runde eine neue Karte erstellen, indem sie eine Funktion auf die vorherige anwenden, während die grafische OCaml-Version zwei Arrays verwendet und diese abwechselnd aus Geschwindigkeitsgründen aktualisiert.
Einige der Implementierungen zerlegen die Kartenaktualisierungsfunktion in Funktionen zum Zählen der Nachbarschaft, Anwenden der Lebensregel und Durchlaufen der Karte. Dies scheinen nützliche Komponenten für ein funktionales Design zu sein. Versuchen Sie, nur die Platine zu modifizieren, und behalten Sie alles andere als reine Funktionen bei.
quelle
Hier ist eine kurze rein funktionale Version in Clojure. Der größte Verdienst geht an Christophe Grand, der dies in seinem Blog-Beitrag " Conway's Game of Life" veröffentlicht hat
Das Spiel kann dann durch wiederholtes Anwenden der "Schritt" -Funktion auf eine Reihe von Zellen gespielt werden, zB:
Die Cleverness ist der Teil (Mapcat-Nachbarzellen). Dabei wird eine Liste mit acht Nachbarn für jede der aktiven Zellen erstellt und alle zusammen verkettet. Dann kann die Häufigkeit, mit der jede Zelle in dieser Liste erscheint, gezählt werden (Frequenzen ....), und schließlich schaffen es diejenigen, die die richtigen Frequenzzählungen haben, zur nächsten Generation.
quelle