Viele moderne Programmiersprachen unterstützen ein Konzept des Schließens , dh eines Codes (eines Blocks oder einer Funktion)
- Kann als Wert behandelt und daher in einer Variablen gespeichert, an verschiedene Teile des Codes weitergegeben, in einem Teil eines Programms definiert und in einem völlig anderen Teil desselben Programms aufgerufen werden.
- Kann Variablen aus dem Kontext erfassen, in dem sie definiert sind, und auf sie zugreifen, wenn sie später aufgerufen werden (möglicherweise in einem völlig anderen Kontext).
Hier ist ein Beispiel für einen Abschluss in Scala:
def filterList(xs: List[Int], lowerBound: Int): List[Int] =
xs.filter(x => x >= lowerBound)
Das Funktionsliteral x => x >= lowerBound
enthält die freie Variable lowerBound
, die durch das Argument der gleichnamigen Funktion geschlossen (gebunden) wird filterList
. Der Abschluss wird an die Bibliotheksmethode übergeben filter
, die ihn als normale Funktion wiederholt aufrufen kann.
Ich habe auf dieser Website viele Fragen und Antworten gelesen, und soweit ich weiß, wird der Begriff Abschluss häufig automatisch mit funktionaler Programmierung und funktionalem Programmierstil assoziiert .
Die Definition der Funktionsprogrammierung auf Wikipedia lautet:
In der Informatik ist die funktionale Programmierung ein Programmierparadigma, das die Berechnung als Bewertung mathematischer Funktionen behandelt und Zustands- und veränderbare Daten vermeidet. Es betont die Anwendung von Funktionen im Gegensatz zum imperativen Programmierstil, der Zustandsänderungen betont.
und weiter
[...] im Funktionscode hängt der Ausgabewert einer Funktion nur von den Argumenten ab, die in die Funktion [...] eingegeben werden. Die Beseitigung von Nebenwirkungen kann das Verständnis und die Vorhersage des Verhaltens eines Programms erheblich erleichtern. Dies ist eine der Hauptmotive für die Entwicklung der funktionalen Programmierung.
Andererseits ermöglichen viele von Programmiersprachen bereitgestellte Abschlusskonstrukte, dass ein Abschluss nicht lokale Variablen erfasst und beim Aufruf des Abschlusses ändert, wodurch ein Nebeneffekt auf die Umgebung erzeugt wird, in der sie definiert wurden.
In diesem Fall implementieren Verschlüsse die erste Idee der funktionalen Programmierung (Funktionen sind erstklassige Entitäten, die wie andere Werte verschoben werden können), vernachlässigen jedoch die zweite Idee (Vermeidung von Nebenwirkungen).
Wird diese Verwendung von Verschlüssen mit Nebenwirkungen als funktionaler Stil betrachtet oder werden Verschlüsse als allgemeineres Konstrukt betrachtet, das sowohl für einen funktionalen als auch für einen nicht funktionalen Programmierstil verwendet werden kann? Gibt es Literatur zu diesem Thema?
WICHTIGE NOTIZ
Ich bezweifle nicht die Nützlichkeit von Nebenwirkungen oder Verschlüssen mit Nebenwirkungen. Ich bin auch nicht an einer Diskussion über die Vor- und Nachteile von Verschlüssen mit oder ohne Nebenwirkungen interessiert.
Ich bin nur daran interessiert zu wissen, ob die Verwendung solcher Verschlüsse vom Befürworter der funktionalen Programmierung immer noch als funktionaler Stil angesehen wird oder ob im Gegenteil von ihrer Verwendung bei Verwendung eines funktionalen Stils abgeraten wird.
quelle
Antworten:
Nein; Bei der Definition des funktionalen Paradigmas geht es um mangelnden Zustand und implizit um fehlende Nebenwirkungen. Es geht nicht um Funktionen höherer Ordnung, Schließungen, sprachunterstützte Listenmanipulation oder andere Sprachfunktionen ...
Der Name der funktionalen Programmierung leitet sich vom mathematischen Begriff der Funktionen ab - wiederholte Aufrufe derselben Eingabe ergeben immer dieselbe Ausgabe - nullipotente Funktion. Dies kann nur erreicht werden, wenn die Daten unveränderlich sind . Um die Entwicklung zu vereinfachen, wurden die Funktionen veränderlich (Funktionen ändern sich, Daten sind immer noch unveränderlich) und damit der Begriff von Funktionen höherer Ordnung (Funktionen in der Mathematik, beispielsweise als Ableitungen) - eine Funktion, die eine andere Funktion als Eingabe verwendet. Für die Möglichkeit, Funktionen herumzutragen und als Argumente weiterzugeben, wurden erstklassige Funktionen übernommen. Im Anschluss daran wurden zur weiteren Steigerung der Produktivität Schließungen vorgenommen .
Dies ist natürlich eine sehr vereinfachte Ansicht.
quelle
map
zum Beispiel eine Funktion, wendet sie auf eine Liste an und gibt eine Liste der Ergebnisse zurück.map
ändert keines seiner Argumente, es ändert nicht das Verhalten der Funktion, die es als Argument verwendet, aber es ist definitiv eine Funktion höherer Ordnung - wenn Sie sie teilweise anwenden, nur mit dem Funktionsparameter, haben Sie eine konstruiert Neue Funktion, die eine Liste bearbeitet, aber noch keine Mutation aufgetreten ist.Nein. "Functional-Style" impliziert eine nebenwirkungsfreie Programmierung.
Um zu sehen, warum, werfen Sie einen Blick auf Eric Lipperts Blogeintrag über die
ForEach<T>
Erweiterungsmethode und warum Microsoft eine solche Sequenzmethode nicht in Linq aufgenommen hat :quelle
ParallelQuery<T>.ForAll(...)
. Die Implementierung eines solchenIEnumerable<T>.ForEach(...)
ist äußerst nützlich für das Debuggen vonForAll
Anweisungen (ersetzen Sie dasForAll
durchForEach
und entfernen Sie das,AsParallel()
und Sie können es viel einfacher durchlaufen / debuggen)Funktionale Programmierung bringt erstklassige Funktionen sicher auf die nächste konzeptionelle Ebene, aber das Deklarieren anonymer Funktionen oder das Übergeben von Funktionen an andere Funktionen ist nicht unbedingt eine funktionale Programmiersache. In C war alles eine ganze Zahl. Eine Zahl, ein Zeiger auf Daten, ein Zeiger auf eine Funktion ... alles nur ints. Sie könnten Funktionszeiger an andere Funktionen übergeben, Listen mit Funktionszeigern erstellen ... Wenn Sie in Assemblersprache arbeiten, sind Funktionen eigentlich nur Adressen im Speicher, in denen Blöcke von Maschinenanweisungen gespeichert sind. Das Vergeben eines Namens für eine Funktion ist ein zusätzlicher Aufwand für die Personen, die einen Compiler zum Schreiben von Code benötigen. Funktionen waren also in diesem Sinne "erstklassig" in einer völlig nicht funktionierenden Sprache.
Wenn Sie nur mathematische Formeln in einer REPL berechnen, können Sie mit Ihrer Sprache funktional rein sein. Die meisten Business-Programme haben jedoch Nebenwirkungen. Ein Nebeneffekt ist es, Geld zu verlieren, während Sie auf den Abschluss eines lang laufenden Programms warten. Das Ausführen externer Maßnahmen: Das Schreiben in eine Datei, das Aktualisieren einer Datenbank, das Protokollieren von Ereignissen in der richtigen Reihenfolge usw. erfordert eine Statusänderung. Wir könnten darüber diskutieren, ob sich der Status wirklich ändert, wenn Sie diese Aktionen in unveränderlichen Wrappern kapseln, die Nebenwirkungen abschrecken, damit sich Ihr Code nicht darum kümmern muss. Aber es ist wie zu diskutieren, ob ein Baum ein Geräusch macht, wenn er in den Wald fällt, ohne dass jemand da ist, der es hört. Tatsache ist, dass der Baum aufrecht begann und auf dem Boden landete. Der Status wird geändert, wenn Dinge erledigt sind, auch wenn nur gemeldet wird, dass die Dinge erledigt wurden.
Wir haben also eine Skala funktionaler Reinheit, nicht Schwarz und Weiß, sondern Grautöne. Und in dieser Größenordnung gilt: Je weniger Nebenwirkungen, desto geringer die Veränderlichkeit, desto besser (funktioneller).
Wenn Sie in Ihrem ansonsten funktionalen Code unbedingt Nebenwirkungen oder einen veränderlichen Zustand benötigen, bemühen Sie sich, diese oder den Rest Ihres Programms so gut wie möglich zu kapseln. Die Verwendung eines Verschlusses (oder irgendetwas anderem), um Nebenwirkungen oder veränderlichen Zustand in ansonsten reine Funktionen zu injizieren, ist das Gegenteil der funktionalen Programmierung. Die einzige Ausnahme könnte sein, wenn der Abschluss der effektivste Weg wäre, die Nebenwirkungen des Codes, an den er übergeben wird, zu kapseln. Es ist immer noch keine "funktionale Programmierung", aber es könnte der Schrank sein, den Sie in bestimmten Situationen bekommen können.
quelle