Die zwei Bedingungen, die eine Funktion pure
wie folgt definieren, sind wie folgt:
- Keine Nebenwirkungen (dh nur Änderungen des lokalen Bereichs sind zulässig)
- Geben Sie immer den gleichen Ausgang zurück, wenn Sie den gleichen Eingang haben
Wenn die erste Bedingung immer wahr ist, gibt es Zeiten, in denen die zweite Bedingung nicht wahr ist?
Dh ist es wirklich nur bei der ersten Bedingung notwendig?
Antworten:
Hier einige Gegenbeispiele, die den äußeren Umfang nicht verändern, aber dennoch als unrein gelten:
function a() { return Date.now(); }
function b() { return window.globalMutableVar; }
function c() { return document.getElementById("myInput").value; }
function d() { return Math.random(); }
(was zwar das PRNG ändert, aber nicht als beobachtbar angesehen wird)Der Zugriff auf nicht konstante nicht lokale Variablen reicht aus, um die zweite Bedingung verletzen zu können.
Ich halte die beiden Reinheitsbedingungen immer für komplementär:
Der Begriff Nebenwirkung bezieht sich nur auf die erste, die Funktion, die den nicht lokalen Zustand modifiziert. Manchmal werden Lesevorgänge jedoch auch als Nebenwirkungen betrachtet: Wenn es sich um Vorgänge handelt, die auch das Schreiben umfassen, selbst wenn ihr Hauptzweck darin besteht, auf einen Wert zuzugreifen. Beispiele hierfür sind das Erzeugen einer Pseudozufallszahl, die den internen Zustand des Generators ändert, das Lesen aus einem Eingangsstrom, der die Leseposition vorverlegt, oder das Lesen von einem externen Sensor, der einen Befehl "Messung durchführen" beinhaltet.
quelle
prompt("you choose")
es keine Nebenwirkungen gibt, sollten wir einen Schritt zurücktreten und die Bedeutung der Nebenwirkungen klären.Die "normale" Art zu formulieren, was eine reine Funktion ist, ist die referenzielle Transparenz . Eine Funktion ist rein, wenn sie referenziell transparent ist .
Referentielle Transparenz bedeutet ungefähr, dass Sie den Aufruf der Funktion an jeder Stelle im Programm durch ihren Rückgabewert ersetzen können oder umgekehrt, ohne die Bedeutung des Programms zu ändern.
Wenn beispielsweise
printf
Cs referenziell transparent wären , sollten diese beiden Programme dieselbe Bedeutung haben:und
und alle folgenden Programme sollten dieselbe Bedeutung haben:
Weil
printf
die Anzahl der geschriebenen Zeichen zurückgibt, in diesem Fall 5.Bei
void
Funktionen wird es noch deutlicher . Wenn ich eine Funktion habevoid foo
, dannsollte das gleiche sein wie
Das heißt, da
foo
nichts zurückgegeben wird, sollte ich es durch nichts ersetzen können, ohne die Bedeutung des Programms zu ändern.Es ist also klar, dass weder
printf
nochfoo
referenziell transparent sind und somit keiner von ihnen rein ist. Tatsächlich kann einevoid
Funktion niemals referenziell transparent sein, es sei denn, es handelt sich um ein No-Op.Ich finde diese Definition viel einfacher zu handhaben als die, die Sie gegeben haben. Sie können es auch in jeder gewünschten Granularität anwenden: Sie können es auf einzelne Ausdrücke, Funktionen und ganze Programme anwenden. So können Sie beispielsweise über eine Funktion wie diese sprechen:
Wir können die Ausdrücke analysieren, aus denen die Funktion besteht, und leicht den Schluss ziehen, dass sie nicht referenziell transparent und daher nicht rein sind, da sie eine veränderbare Datenstruktur verwenden, nämlich das
memo
Array. Wir können jedoch auf die Funktion auch einen Blick und sehen , dass es ist referentiell transparent und somit rein. Dies wird manchmal als äußere Reinheit bezeichnet , dh eine Funktion, die der Außenwelt rein erscheint, aber intern unrein implementiert wird.Solche Funktionen sind immer noch nützlich, da die Verunreinigung zwar alles um sie herum infiziert, die externe reine Schnittstelle jedoch eine Art "Reinheitsbarriere" bildet, bei der die Verunreinigung nur die drei Zeilen der Funktion infiziert, aber nicht in den Rest des Programms gelangt . Diese drei Zeilen sind viel einfacher auf Korrektheit zu analysieren als das gesamte Programm.
quelle
memo[n]
ist idempotent, und das Nichtlesen verschwendet lediglich CPU-Zyklen.memo[n] = ...
zuerst einen Wörterbucheintrag und speichert dann den Wert darin. Dadurch bleibt ein Fenster, in dem ein anderer Thread einen nicht initialisierten Eintrag sehen kann.Es scheint mir, dass die zweite Bedingung, die Sie beschrieben haben, eine schwächere Einschränkung ist als die erste.
Lassen Sie mich ein Beispiel geben. Angenommen, Sie haben eine Funktion zum Hinzufügen einer Funktion, die sich auch an der Konsole anmeldet:
Die zweite von Ihnen angegebene Bedingung ist erfüllt: Diese Funktion gibt immer dieselbe Ausgabe zurück, wenn dieselbe Eingabe gegeben wird. Dies ist jedoch keine reine Funktion, da sie den Nebeneffekt der Protokollierung an der Konsole enthält.
Eine reine Funktion ist streng genommen eine Funktion, die die Eigenschaft der referenziellen Transparenz erfüllt . Mit dieser Eigenschaft können wir eine Funktionsanwendung durch den von ihr erzeugten Wert ersetzen, ohne das Verhalten des Programms zu ändern.
Angenommen, wir haben eine Funktion, die einfach Folgendes hinzufügt:
Wir können ersetzen
addOne(5)
mit6
überall in unserem Programm und wird sich nichts ändern.Im Gegensatz dazu können wir nirgendwo in unserem Programm durch
addOneAndLog(x)
den Wert ersetzen6
, ohne das Verhalten zu ändern, da der erste Ausdruck dazu führt, dass etwas in die Konsole geschrieben wird, während der zweite dies nicht tut.Wir betrachten jedes dieser zusätzlichen Verhaltensweisen,
addOneAndLog(x)
die neben der Rückgabe der Ausgabe auftreten, als Nebeneffekt .quelle
Date.now()
ist nicht rein / referenziell transparent, aber nicht, weil es Nebenwirkungen hat, sondern weil sein Ergebnis von mehr als nur seiner Eingabe abhängt.Es kann eine Zufallsquelle von außerhalb des Systems geben. Angenommen, ein Teil Ihrer Berechnung enthält die Raumtemperatur. Die Ausführung der Funktion führt dann jedes Mal zu unterschiedlichen Ergebnissen, abhängig vom zufälligen externen Element der Raumtemperatur. Der Status wird durch Ausführen des Programms nicht geändert.
Jedenfalls kann ich mir nur vorstellen.
quelle
Das Problem bei FP-Definitionen ist, dass sie sehr künstlich sind. Jede Bewertung / Berechnung hat Nebenwirkungen auf den Bewerter. Es ist theoretisch wahr. Eine Ablehnung zeigt nur, dass FP-Apologeten Philosophie und Logik ignorieren: Eine "Bewertung" bedeutet, den Zustand einer intelligenten Umgebung (Maschine, Gehirn usw.) zu ändern. Dies ist die Art des Bewertungsprozesses. Keine Änderungen - keine "Kalküle". Der Effekt kann sehr gut sichtbar sein: Erhitzen der CPU oder deren Ausfall, Herunterfahren des Motherboards bei Überhitzung usw.
Wenn Sie über referenzielle Transparenz sprechen, sollten Sie verstehen, dass Informationen über diese Transparenz für den Menschen als Schöpfer des gesamten Systems und Inhaber semantischer Informationen verfügbar sind und möglicherweise nicht für den Compiler verfügbar sind. Beispielsweise kann eine Funktion eine externe Ressource lesen und hat eine E / A-Monade in ihrer Signatur, gibt jedoch immer den gleichen Wert zurück (z. B. das Ergebnis von
current_year > 0
). Der Compiler weiß nicht, dass die Funktion immer das gleiche Ergebnis zurückgibt, daher ist die Funktion unrein, hat jedoch eine referenziell transparente Eigenschaft und kann durch eineTrue
Konstante ersetzt werden.Um solche Ungenauigkeiten zu vermeiden, sollten wir mathematische Funktionen und "Funktionen" in Programmiersprachen unterscheiden. Funktionen in Haskell sind immer unrein und die Definition der damit verbundenen Reinheit ist immer sehr bedingt: Sie laufen auf realer Hardware mit realen Nebenwirkungen und physikalischen Eigenschaften, was für mathematische Funktionen falsch ist. Dies bedeutet, dass das Beispiel mit der Funktion "printf" völlig falsch ist.
Aber nicht alle mathematischen Funktionen sind auch rein: Jede Funktion, die
t
(Zeit) als Parameter hat, kann unrein sein: Siet
enthält alle Effekte und die stochastische Natur der Funktion: Im allgemeinen Fall haben Sie ein Eingangssignal und keine Ahnung von tatsächlichen Werten sei sogar ein Geräusch.quelle
Ja
Betrachten Sie unten ein einfaches Code-Snippet
Dieser Code gibt eine zufällige Ausgabe für denselben gegebenen Satz von Eingaben zurück - hat jedoch keine Nebenwirkungen.
Der Gesamteffekt der beiden Punkte Nr. 1 und Nr. 2, die Sie in Kombination erwähnt haben, bedeutet: Zu jedem Zeitpunkt, wenn die Funktion
Sum
mit derselben E / A durch das Ergebnis in einem Programm ersetzt wird, ändert sich die Gesamtbedeutung des Programms nicht . Dies ist nichts anderes als referenzielle Transparenz .quelle
rnd
entgeht der Funktion nicht, daher spielt die Tatsache, dass sich ihr Status ändert, keine Rolle für die Reinheit der Funktion, aber die Tatsache, dass derRandom
Konstruktor die aktuelle Zeit als Startwert verwendet, bedeutet, dass es andere "Eingaben" alsa
und gibtb
.