Wie nennt man eine Funktion, bei der dieselbe Eingabe immer dieselbe Ausgabe zurückgibt, aber auch Nebenwirkungen hat?


43

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?


91
Msgstr "Keine reine Funktion".
Ross Patterson

2
@ RossPatterson dachte ich auch, aber als ich gefragt wurde, erfuhr ich etwas über referentielle Transparenz, und ich bin froh, dass ich das nicht für mich behalten habe.
m0meni

9
Wenn dies writeToDatabasefehlschlägt, kann dies eine Ausnahme auslösen, sodass Ihre zweite addFunktion manchmal auch dann eine Ausnahme auslöst , wenn sie mit denselben Argumenten aufgerufen wird, die zuvor keine Probleme hatten "Input-Output-Reinheit".
Bakuriu

25
Etwas, das für eine bestimmte Eingabe immer die gleiche Ausgabe liefert, wird als deterministisch bezeichnet .
njzk2

2
@ njzk2: Stimmt, aber es ist auch zustandslos . Eine zustandsbestimmende deterministische Funktion gibt möglicherweise nicht für jeden Eingang den gleichen Ausgang aus. Beispiel: F(x)Gibt zurück, truewenn 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ür F(2).
MSalters

Antworten:


85

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 addist nicht rein . Also als Antwort auf deine Frage würde ich es "unrein" nennen;)

Nach dieser Definition ist eine reine Funktion eine Funktion, die:

  1. Kommt nur auf seine Eingabe an. Das heißt, bei gleicher Eingabe wird immer die gleiche Ausgabe zurückgegeben.
  2. Ist referenziell transparent: Die Funktion kann frei durch ihren Wert ersetzt werden und das "Verhalten" des Programms ändert sich nicht.

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:

function f(a, b) { 
    return add(a, b) + add(a, b);
}

und

function g(a, b) {
    c = add(a, b);
    return c + c;
}

Dies liegt daran, dass obwohl beide Funktionen den gleichen Wert zurückgeben, die Funktion fzweimal in die Datenbank gschreibt, 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 von addnicht "rein" ist.

Wenn Schreibvorgänge in die Datenbank kein feststellbarer Bestandteil des Verhaltens Ihres Programms sind, können beide Versionen von addals 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!


1
Ist "hängt nur von seiner Eingabe ab" nicht redundant bei referentieller Transparenz? Was würde bedeuten, dass RT gleichbedeutend mit Reinheit ist? (Ich bin immer mehr verwirrt, je mehr Quellen ich nachschaue)
Ixrec

Ich stimme zu, es ist verwirrend. Ich kann nur an erfundene Beispiele denken. Sprich f(x)hängt nicht nur von x, sondern auch von einer externen globalen Variablen ab y. Wenn fdann die Eigenschaft von RT vorliegt, können Sie alle Vorkommen mit dem Rückgabewert austauschen , solange Sie nicht berühren y . Ja, mein Beispiel ist zweifelhaft. Aber das Wichtigste ist: Wenn fSchreiben in die Datenbank (oder schreibt in ein Protokoll) ist es die Eigenschaft RT verliert: Jetzt spielt es keine Rolle , ob Sie global verlassen yunberührt, Sie kennen die Bedeutung des Programms ändert sich je nachdem , ob Sie tatsächlich call foder benutze einfach seinen Rückgabewert.
Andres F.

Humph. Nehmen wir an, wir haben eine solche Funktion, die rein ist, abgesehen von Nebenwirkungen, und die garantiert auch ein solches Verhalten aufweist, wenn Ihre beiden Proben gleichwertig sind. (Ich hatte diesen Fall in der Tat, so ist es nicht hypothetisch.) Ich denke, wir sind noch nicht ganz fertig.
Joshua

2
Ich würde behaupten, dass die zweite Funktion auch Regel # 1 brechen könnte. Abhängig von der Sprache und den Fehlerbehandlungsmethoden der verwendeten Datenbank-API gibt die Funktion möglicherweise überhaupt nichts zurück, wenn die Datenbank nicht verfügbar ist oder das Schreiben der Datenbank aus irgendeinem Grund fehlschlägt.
Zach Lipton

1
Da Haskell erwähnt wurde: In Haskell erfordert das Hinzufügen eines solchen Nebeneffekts das Ändern der Signatur der Funktion . (Stellen Sie sich vor, Sie geben die ursprüngliche Datenbank als zusätzliche Eingabe und die geänderte Datenbank als zusätzlichen Rückgabewert der Funktion zurück.) Es ist tatsächlich möglich, Nebenwirkungen im Typensystem auf diese Weise ziemlich elegant zu modellieren, nur dass die heutigen Hauptsprachen sich nicht genug für Nebenwirkungen und Reinheit interessieren, um dies zu tun.
ComicSansMS

19

Wie nennt man eine Funktion, [für die] dieselbe Eingabe immer dieselbe Ausgabe zurückgibt, aber auch Nebenwirkungen hat?

Eine solche Funktion heißt

deterministisch

Ein Algorithmus, dessen Verhalten anhand der Eingabe vollständig vorhergesagt werden kann.

termwiki.com

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 aussehen f(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.

Funktion pur

  1. Die Funktion wertet bei gleichen Argumentwerten immer den gleichen Ergebniswert aus. Der Funktionsergebniswert kann weder von verborgenen Informationen oder Zuständen abhängen, die sich während der Programmausführung oder zwischen verschiedenen Programmausführungen ändern, noch von externen Eingaben von E / A-Geräten.

  2. Die Auswertung des Ergebnisses verursacht keine semantisch beobachtbaren Nebenwirkungen oder Ausgaben, wie z. B. Mutationen von veränderlichen Objekten oder Ausgaben an E / A-Geräte.

wikipedia.org

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.


-1. Das Schreiben in die Datenbank hängt vom externen Status ab, der im Allgemeinen anhand der Eingaben nicht bestimmt werden kann. Die Datenbank ist möglicherweise aus mehreren Gründen nicht verfügbar und es ist nicht vorhersehbar, ob der Vorgang erfolgreich ist. Dies ist kein deterministisches Verhalten.
Frax

@Frax Systemspeicher ist möglicherweise nicht verfügbar. Die CPU ist möglicherweise nicht verfügbar. Deterministisch zu sein, garantiert keinen Erfolg. Es garantiert, dass erfolgreiches Verhalten vorhersehbar ist.
candied_orange

OOMing ist für keine Funktion spezifisch, es handelt sich um eine andere Problemkategorie. Schauen wir uns nun Punkt 1 Ihrer Definition der "reinen Funktion" an (was in der Tat "deterministisch" bedeutet): "Der Funktionsergebniswert kann nicht von versteckten Informationen oder Zuständen abhängen , die sich während der Programmausführung oder zwischen verschiedenen Ausführungen von ändern können Das Programm kann auch nicht von externen Eingaben von E / A-Geräten abhängen. " Die Datenbank ist ein solcher Zustand, daher erfüllt die OP-Funktion diese Bedingung eindeutig nicht - sie ist nicht deterministisch.
Frax

@candied_orange Ich würde zustimmen, wenn das Schreiben in die DB nur von den Eingaben abhängig wäre. Aber es ist so 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.
Marstato

1
@candied_orange Ihre Angabe von deterministischen Zuständen "ein Algorithmus, dessen Verhalten vollständig aus der Eingabe vorhergesagt werden kann." Schreiben an IO ist für mich definitiv Verhalten, nicht Ergebnis.
Marstato

9

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.


3
Der Begriff "referenziell transparent" ist strenger definiert als "jedermann interessiert". auch wenn wir IO Probleme wie Verbindungsprobleme außer Acht lassen, fehlende Verbindungszeichenfolgen, Timeouts usw., dann ist es immer noch leicht zu zeigen , dass ein Programm , das ersetzt (f x, f x)mit let 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 als new Random().Next()referenziell transparent bezeichnen, denn es ist mir egal, welche Nummer ich sowieso erhalte.
Sara

@kai: Abhängig vom Kontext können Sie Nebenwirkungen ignorieren. Andererseits ist der Rückgabewert einer Funktion wie random kein Nebeneffekt, sondern ihr Haupteffekt.
Giorgio

@ Giorgio Random.Nextin .NET hat in der Tat Nebenwirkungen. Besonders gern. Wenn Sie es können Next, weisen Sie es einer Variablen zu und rufen Sie es dann Nexterneut auf und weisen Sie es einer anderen Variablen zu. Es besteht die Möglichkeit, dass sie nicht gleich sind. Warum? Weil das Aufrufen Nexteinen versteckten internen Zustand im RandomObjekt ä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.
Sara

3

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.


1
Einverstanden! Es ist (informell) die mathematische Definition von "Funktion", aber wie Sie sagen, bedeutet "Funktion" in Programmiersprachen leider etwas anderes, als "eine schrittweise Prozedur, die erforderlich ist, um einen Wert zu erhalten".
Andres F.

2

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.


Es hängt davon ab. Vielleicht machen die Protokolle es unrein, zum Beispiel, wenn es Ihnen wichtig ist, wie oft und zu welcher Zeit "INFO f () called" in Ihrem Protokoll angezeigt wird. Was du oft tust :)
Andres F.

8
-1 Protokolle spielen eine Rolle. Auf den meisten Plattformen synchronisiert die Ausgabe jeder Art implizit den Ausführungsthread. Das Verhalten Ihres Programms hängt von anderen Thread-Schreibvorgängen, externen Protokollschreibern, manchmal von Protokolllesevorgängen und dem Status von Dateideskriptoren ab. Es ist so rein wie ein Eimer Schmutz.
Basilevs

@AndresF. Nun, Sie interessieren sich wahrscheinlich nicht für die buchstäbliche Anzahl von Malen. Sie kümmern sich wahrscheinlich nur darum, dass es so oft protokolliert wird, wie die Funktion aufgerufen wurde.
DeadMG

@Basilevs Das Verhalten der Funktion hängt überhaupt nicht von ihnen ab. Wenn das Schreiben des Protokolls fehlschlägt, fahren Sie einfach fort.
DeadMG

2
Es ist eine Frage, ob Sie den Logger als Teil der Ausführungsumgebung definieren oder nicht. Ist zum Beispiel meine reine Funktion immer noch rein, wenn ich einen Debugger an den Prozess anhänge und einen Haltepunkt darauf setze? Aus der Sicht der Person, die den Debugger verwendet, hat die Funktion offensichtlich Nebenwirkungen, aber normalerweise analysieren wir ein Programm mit der Konvention, dass dies "nicht zählt". Das Gleiche kann (muss aber nicht) für die Protokollierung gelten, die zum Debuggen verwendet wird. Vermutlich ist dies der Grund, warum Trace seine Unreinheit verbirgt. Eine wichtige Nebenwirkung ist natürlich die unternehmenskritische Protokollierung, z. B. für Audits .
Steve Jessop

1

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:

  • Hängt die Nebenwirkung vom Argument der Funktion oder vom Ergebnis der Nebenwirkung ab?
    • Nein: Die "wirksame Funktion" kann zu einer reinen Funktion, einer wirksamen Aktion und einem Mechanismus zu deren Kombination umgestaltet werden.
    • Ja: Die "effektive Funktion" ist eine Funktion, die ein monadisches Ergebnis erzeugt.

Dies ist in Haskell einfach zu veranschaulichen (und dieser Satz ist nur ein halber Witz). Ein Beispiel für den "Nein" -Fall wäre:

double :: Num a => a -> IO a
double x = do
  putStrLn "I'm doubling some number"
  return (x*2)

In diesem Beispiel hat die Aktion, die wir ausführen (die Zeile drucken "I'm doubling some number"), keinen Einfluss auf die Beziehung zwischen xund dem Ergebnis. Dies bedeutet, dass wir es auf diese Weise umgestalten können (unter Verwendung der ApplicativeKlasse und ihres *>Operators), was zeigt, dass die Funktion und der Effekt tatsächlich orthogonal sind:

double :: Num a => a -> IO a
double x = action *> pure (function x)
  where
    -- The pure function 
    function x = x*2  
    -- The side effect
    action = putStrLn "I'm doubling some number"

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:

double :: Num a => a -> IO a
double x = do
  putStrLn ("I'm doubling the number " ++ show x)
  return (x*2)

Nun hängt die Zeichenfolge, die Sie drucken, vom Wert von ab x. Der Funktionsteil (multiplizieren xmit zwei) hängt jedoch überhaupt nicht vom Effekt ab, sodass wir ihn trotzdem ausklammern können:

logged :: (a -> b) -> (a -> IO x) -> IO b
logged function logger a = do
  logger a
  return (function a)

double x = logged function logger
  where function = (*2) 
        logger x putStrLn ("I'm doubling the number " ++ show x)

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 MonadKlasse so ausgiebig nutzt . Monaden sind unter anderem ein Werkzeug, um diese Art von Analyse und Refactoring durchzuführen.


-2

Funktionen, die Nebenwirkungen hervorrufen sollen, werden oft als wirksam bezeichnet . Beispiel https://slpopejoy.github.io/posts/Effectful01.html


Erwähnen Sie nur, dass der allgemein anerkannte Begriff wirksam ist und er nach unten gewählt wird ... Ignoranz ist wohl ein Glück. ..
Ben Hutchison

"effektiv" ist ein Wort, das der Autor dieses Beitrags als "Nebenwirkungen" bezeichnet hat. Er sagt es selbst.
Robert Harvey

Die effektive Funktion des Googelns zeigt zahlreiche Beweise, die häufig verwendet werden. Der Blogbeitrag wurde als eines von vielen Beispielen angeführt, nicht als Definition. In den funktionalen Programmierkreisen, in denen reine Funktionen die Standardeinstellung sind, ist ein positiver Begriff erforderlich, um bewusst nebenwirkende Funktionen zu beschreiben. Dh mehr als nur das Fehlen von Reinheit . Wirksam ist dieser Begriff. Betrachten Sie sich jetzt als gebildet.
Ben Hutchison

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.