Angenommen, wir haben eine normale reine Funktion wie z
function add(a, b) {
return a + b
}
Und dann ändern wir es so, dass es eine Nebenwirkung hat
function add(a, b) {
writeToDatabase(Math.random())
return a + b;
}
Soweit ich weiß, wird es nicht als reine Funktion angesehen, weil ich oft Leute höre, die reine Funktionen als "Funktionen ohne Nebenwirkungen" bezeichnen. Es verhält sich jedoch wie eine reine Funktion, sofern es für dieselben Eingaben dieselbe Ausgabe zurückgibt.
Gibt es einen anderen Namen für diese Art von Funktion, ist er unbenannt oder ist er immer noch rein und ich irre mich über die Definition von Reinheit?
writeToDatabase
fehlschlägt, kann dies eine Ausnahme auslösen, sodass Ihre zweiteadd
Funktion manchmal auch dann eine Ausnahme auslöst , wenn sie mit denselben Argumenten aufgerufen wird, die zuvor keine Probleme hatten "Input-Output-Reinheit".F(x)
Gibt zurück,true
wenn es mit demselben Argument wie der vorherige Aufruf aufgerufen wurde. Bei der Sequenz ist{1,2,2} => {undefined, false, true}
dies eindeutig deterministisch, es gibt jedoch unterschiedliche Ausgaben fürF(2)
.Antworten:
Ich bin mir nicht sicher, ob es sich um eine universelle Definition von Reinheit handelt, aber aus Sicht von Haskell (einer Sprache, in der sich Programmierer für Dinge wie Reinheit und referenzielle Transparenz interessieren) ist nur die erste Ihrer Funktionen "rein". Die zweite Version von
add
ist nicht rein . Also als Antwort auf deine Frage würde ich es "unrein" nennen;)Nach dieser Definition ist eine reine Funktion eine Funktion, die:
Mit dieser Definition ist klar, dass Ihre zweite Funktion nicht als rein betrachtet werden kann, da sie gegen Regel 2 verstößt. Das heißt, die folgenden beiden Programme sind NICHT äquivalent:
und
Dies liegt daran, dass obwohl beide Funktionen den gleichen Wert zurückgeben, die Funktion
f
zweimal in die Datenbankg
schreibt, jedoch einmal! Es ist sehr wahrscheinlich, dass Schreibvorgänge in die Datenbank Teil des beobachtbaren Verhaltens Ihres Programms sind. In diesem Fall habe ich gezeigt, dass Ihre zweite Version vonadd
nicht "rein" ist.Wenn Schreibvorgänge in die Datenbank kein feststellbarer Bestandteil des Verhaltens Ihres Programms sind, können beide Versionen von
add
als gleichwertig und rein betrachtet werden. Aber ich kann mir kein Szenario vorstellen, in dem das Schreiben in die Datenbank keine Rolle spielt. Auch das Protokollieren spielt eine Rolle!quelle
f(x)
hängt nicht nur vonx
, sondern auch von einer externen globalen Variablen aby
. Wennf
dann die Eigenschaft von RT vorliegt, können Sie alle Vorkommen mit dem Rückgabewert austauschen , solange Sie nicht berühreny
. Ja, mein Beispiel ist zweifelhaft. Aber das Wichtigste ist: Wennf
Schreiben in die Datenbank (oder schreibt in ein Protokoll) ist es die Eigenschaft RT verliert: Jetzt spielt es keine Rolle , ob Sie global verlasseny
unberührt, Sie kennen die Bedeutung des Programms ändert sich je nachdem , ob Sie tatsächlich callf
oder benutze einfach seinen Rückgabewert.Eine solche Funktion heißt
Zum Stand:
Abhängig von der Definition einer von Ihnen verwendeten Funktion hat eine Funktion keinen Status. Wenn Sie aus der objektorientierten Welt kommen, denken Sie daran, dass dies
x.f(y)
eine Methode ist. Als Funktion würde es so aussehenf(x,y)
. Und wenn Sie in Abschlüsse mit eingeschlossenem lexikalischem Gültigkeitsbereich verwickelt sind, denken Sie daran, dass der unveränderliche Zustand ebenso gut Teil des Funktionsausdrucks sein kann. Es ist nur ein veränderlicher Zustand, der die deterministische Natur der Funktionen beeinflussen würde. Also ist f (x) = x + 1 deterministisch, solange sich die 1 nicht ändert. Egal wo die 1 gespeichert ist.Ihre Funktionen sind beide deterministisch. Deine erste ist auch eine reine Funktion. Dein zweiter ist nicht rein.
Punkt 1 bedeutet deterministisch . Punkt 2 bedeutet referentielle Transparenz . Zusammen bedeuten sie, dass eine reine Funktion nur ihre Argumente und ihren zurückgegebenen Wert ändern kann. Sonst ändert sich nichts. Sonst wird nichts verändert.
quelle
Math.random()
. Also nein, es sei denn, wir nehmen ein PRNG (anstelle eines physischen RNG) an UND betrachten, dass PRNGs einen Teil der Eingabe angeben (was nicht der Fall ist, die Referenz ist fest codiert), ist es nicht deterministisch.Wenn Ihnen die Nebenwirkung egal ist, ist sie referenziell transparent. Es ist natürlich möglich, dass Sie sich nicht darum kümmern, aber jemand anderes. Die Anwendbarkeit des Begriffs ist also kontextabhängig.
Ich kenne keinen allgemeinen Begriff für genau die Eigenschaften, die Sie beschreiben, aber eine wichtige Untergruppe sind diejenigen, die idempotent sind . In der Informatik ist eine idempotente Funktion etwas anders als in der Mathematik * und kann mit demselben Effekt wiederholt werden. Das heißt, das Netto-Nebeneffekt-Ergebnis, wenn man es viele Male macht, ist dasselbe wie wenn man es einmal macht.
Wenn Ihre Nebenwirkung darin bestand, eine Datenbank mit einem bestimmten Wert in einer bestimmten Zeile zu aktualisieren oder eine Datei mit genau konsistenten Inhalten zu erstellen, wäre dies idempotent , würde sie jedoch zur Datenbank hinzugefügt oder an eine Datei angehängt , dann würde es nicht.
Kombinationen von idempotenten Funktionen können insgesamt idempotent sein oder auch nicht.
* Die Verwendung von idempotent in der Informatik anders als in der Mathematik scheint auf eine falsche Verwendung des mathematischen Begriffs zurückzuführen zu sein, der damals angenommen wurde, weil das Konzept nützlich ist.
quelle
(f x, f x)
mitlet y = f x in (y, y)
Sie können in den Speicherplatz-Ausnahmen doppelt so schnell laufen wird argumentieren , dass diese Randfälle, die Sie nicht interessieren, aber mit einer solchen Fuzzy-Definition können wir sie genauso gut alsnew Random().Next()
referenziell transparent bezeichnen, denn es ist mir egal, welche Nummer ich sowieso erhalte.Random.Next
in .NET hat in der Tat Nebenwirkungen. Besonders gern. Wenn Sie es könnenNext
, weisen Sie es einer Variablen zu und rufen Sie es dannNext
erneut auf und weisen Sie es einer anderen Variablen zu. Es besteht die Möglichkeit, dass sie nicht gleich sind. Warum? Weil das AufrufenNext
einen versteckten internen Zustand imRandom
Objekt ändert . Dies ist das genaue Gegenteil von referentieller Transparenz. Ich verstehe Ihre Behauptung nicht, dass "Haupteffekte" keine Nebenwirkungen sein können. In imperativem Code ist der Haupteffekt häufig ein Nebeneffekt, da imperative Programme von Natur aus statusbehaftet sind.Ich weiß nicht, wie solche Funktionen aufgerufen werden (oder ob es überhaupt einen systematischen Namen gibt), aber ich würde eine Funktion aufrufen, die nicht rein ist (da andere Antworten kauerten), aber immer das gleiche Ergebnis liefert, wenn sie mit den gleichen Parametern versorgt wird parameter "(verglichen mit der Funktion seiner Parameter und einem anderen Zustand). Ich würde es einfach Funktion nennen, aber wenn wir im Kontext der Programmierung "Funktion" sagen, meinen wir leider etwas, das überhaupt keine tatsächliche Funktion sein muss.
quelle
Es hängt im Wesentlichen davon ab, ob Ihnen die Verunreinigung wichtig ist oder nicht. Wenn die Semantik dieser Tabelle so ist, dass es Ihnen egal ist, wie viele Einträge es gibt, dann ist es rein. Sonst ist es nicht rein.
Oder anders ausgedrückt, es ist in Ordnung, solange Optimierungen, die auf Reinheit basieren, die Programmsemantik nicht beeinträchtigen.
Ein realistischeres Beispiel wäre, wenn Sie versuchen würden, diese Funktion zu debuggen und Protokollanweisungen hinzuzufügen. Technisch ist die Protokollierung ein Nebeneffekt. Machen die Protokolle es unrein? Nein.
quelle
Ich würde sagen, das Beste, was wir fragen sollten, ist nicht, wie wir es nennen würden, sondern wie wir ein solches Stück Code analysieren würden . Und meine erste Schlüsselfrage bei einer solchen Analyse wäre:
Dies ist in Haskell einfach zu veranschaulichen (und dieser Satz ist nur ein halber Witz). Ein Beispiel für den "Nein" -Fall wäre:
In diesem Beispiel hat die Aktion, die wir ausführen (die Zeile drucken
"I'm doubling some number"
), keinen Einfluss auf die Beziehung zwischenx
und dem Ergebnis. Dies bedeutet, dass wir es auf diese Weise umgestalten können (unter Verwendung derApplicative
Klasse und ihres*>
Operators), was zeigt, dass die Funktion und der Effekt tatsächlich orthogonal sind:In diesem Fall würde ich persönlich sagen, dass Sie eine reine Funktion herausrechnen können. Bei vielen Haskell-Programmen geht es darum, wie man die reinen Teile aus dem effektiven Code herausrechnet.
Ein Beispiel für die Art "Ja", bei der der reine und der wirksame Teil nicht orthogonal sind:
Nun hängt die Zeichenfolge, die Sie drucken, vom Wert von ab
x
. Der Funktionsteil (multiplizierenx
mit zwei) hängt jedoch überhaupt nicht vom Effekt ab, sodass wir ihn trotzdem ausklammern können:Ich könnte weitere Beispiele nennen, aber ich hoffe, dies ist genug, um den Ausgangspunkt zu verdeutlichen: Man nennt es nicht "etwas", man analysiert, wie die reinen und die wirksamen Teile zusammenhängen und faktorisiert sie, wenn es so ist zu Ihrem Vorteil.
Dies ist einer der Gründe, warum Haskell seine
Monad
Klasse so ausgiebig nutzt . Monaden sind unter anderem ein Werkzeug, um diese Art von Analyse und Refactoring durchzuführen.quelle
Funktionen, die Nebenwirkungen hervorrufen sollen, werden oft als wirksam bezeichnet . Beispiel https://slpopejoy.github.io/posts/Effectful01.html
quelle