Hier sind meine Argumente, warum funktionale Programmierung für die Computerwissenschaft verwendet werden kann und sollte . Die Vorteile sind enorm und die Nachteile verschwinden schnell. In meinen Augen gibt es nur einen Nachteil:
Con : mangelnde Sprachunterstützung in C / C ++ / Fortran
Zumindest in C ++ verschwindet dieser Nachteil - da C ++ 14/17 leistungsstarke Funktionen zur Unterstützung der funktionalen Programmierung hinzugefügt hat. Möglicherweise müssen Sie selbst Bibliotheks- / Support-Code schreiben, aber die Sprache wird Ihr Freund sein. Als Beispiel sehen Sie hier eine (Warnung: Plug-) Bibliothek, die unveränderliche mehrdimensionale Arrays in C ++ ausführt : https://github.com/jzrake/ndarray-v2 .
Hier ist auch ein Link zu einem guten Buch über funktionale Programmierung in C ++, obwohl es nicht auf wissenschaftliche Anwendungen ausgerichtet ist.
Hier ist meine Zusammenfassung dessen, was ich für die Profis halte:
Vorteile :
- Richtigkeit
- Verständlichkeit
- Performance
In Bezug auf die Korrektheit sind Funktionsprogramme offensichtlich gut aufgestellt : Sie zwingen Sie dazu, den minimalen Zustand Ihrer physikalischen Variablen und die Funktion, die diesen Zustand zeitlich vorantreibt, richtig zu definieren:
int main()
{
auto state = initial_condition();
while (should_continue(state))
{
state = advance(state);
side_effects(state);
}
return 0;
}
Das Lösen einer partiellen Differentialgleichung (oder ODE) ist perfekt für die funktionale Programmierung. Sie wenden lediglich eine pure function ( advance
) auf die aktuelle Lösung an, um die nächste zu generieren.
Nach meiner Erfahrung ist Physiksimulationssoftware im Großen und Ganzen durch schlechtes Staatsmanagement belastet . Normalerweise arbeitet jede Stufe des Algorithmus mit einem Teil eines gemeinsam genutzten (effektiv globalen) Zustands. Dies macht es schwierig oder sogar unmöglich, die richtige Reihenfolge der Vorgänge zu gewährleisten, sodass die Software anfällig für Fehler ist, die sich als Seg-Fehler oder schlimmer als Fehlerausdrücke manifestieren können, die Ihren Code nicht zum Absturz bringen, aber die Integrität seiner Wissenschaft stillschweigend gefährden Ausgabe. Der Versuch, den gemeinsam genutzten Status in einer Physiksimulation zu verwalten, verhindert auch das Multithreading. Dies ist ein Problem für die Zukunft, da Supercomputer eine höhere Kernanzahl anstreben und die Skalierung mit MPI häufig bei ~ 100.000 Aufgaben an erster Stelle steht. Im Gegensatz dazu macht die funktionale Programmierung die Parallelität von gemeinsamem Speicher aufgrund der Unveränderlichkeit trivial.
Die Leistung der funktionalen Programmierung wird auch durch die verzögerte Auswertung von Algorithmen verbessert (in C ++ bedeutet dies, dass zur Kompilierungszeit viele Typen generiert werden - oft einer für jede Anwendung einer Funktion). Es reduziert jedoch den Aufwand für Speicherzugriffe und -zuweisungen und eliminiert den virtuellen Versand. Dadurch kann der Compiler einen gesamten Algorithmus optimieren, indem alle Funktionsobjekte, aus denen er besteht, auf einmal angezeigt werden. In der Praxis werden Sie mit verschiedenen Anordnungen der Auswertungspunkte experimentieren (wobei das Algorithmusergebnis in einem Speicherpuffer zwischengespeichert wird), um die Verwendung von CPU- und Speicherzuordnungen zu optimieren. Dies ist aufgrund der hohen Lokalität (siehe das folgende Beispiel) der Algorithmusstufen im Vergleich zu dem, was Sie normalerweise in einem Modul oder klassenbasiertem Code sehen, recht einfach.
Funktionsprogramme sind insofern leichter zu verstehen , als sie den Aggregatzustand trivialisieren. Das heißt nicht, dass ihre Syntax für alle Ihre Kollegen leicht verständlich ist! Autoren sollten darauf achten, gut benannte Funktionen zu verwenden, und Forscher sollten sich im Allgemeinen daran gewöhnen, Algorithmen eher funktional als prozedural auszudrücken. Ich gebe zu, dass das Fehlen von Kontrollstrukturen für einige abschreckend sein kann, aber ich denke nicht, dass dies uns davon abhalten sollte, in die Zukunft zu gehen, um bessere wissenschaftliche Ergebnisse auf Computern zu erzielen.
Unten finden Sie eine Beispielfunktion advance
, die mithilfe des ndarray-v2
Pakets aus einem Code mit endlichem Volumen angepasst wurde . Beachten Sie die to_shared
Operatoren - dies sind die Bewertungspunkte, auf die ich früher angespielt habe.
auto advance(const solution_state_t& state)
{
auto dt = determine_time_step_size(state);
auto du = state.u
| divide(state.vertices | volume_from_vertices)
| nd::map(recover_primitive)
| extrapolate_boundary_on_axis(0)
| nd::to_shared()
| compute_intercell_flux(0)
| nd::to_shared()
| nd::difference_on_axis(0)
| nd::multiply(-dt * mara::make_area(1.0));
return solution_state_t {
state.time + dt,
state.iteration + 1,
state.vertices,
state.u + du | nd::to_shared() };
}