Ich habe das in imperativen Paradigmen gesehen
f (x) + f (x)
ist möglicherweise nicht dasselbe wie:
2 * f (x)
In einem funktionalen Paradigma sollte es jedoch dasselbe sein. Ich habe versucht, beide Fälle in Python und Schema zu implementieren , aber für mich sehen sie ziemlich einfach aus.
Was wäre ein Beispiel, das den Unterschied zur gegebenen Funktion aufzeigen könnte?
f(x++)+f(x++)
nicht die gleiche sein könnte2*f(x++)
(? in C ist es besonders schön , wenn Sachen wie , dass innerhalb von Makros versteckt ist - habe ich an , dass meine Nase brach wette , Sie)f(x++)+f(x++)
kann absolut alles sein, da es undefiniertes Verhalten hervorruft . Aber das hat nicht wirklich mit referentieller Transparenz zu tun - was für diesen Aufruf nicht hilfreich wäre, ist für referentiell transparente Funktionen wie insin(x++)+sin(x++)
auch undefiniert . Könnte 42 sein, könnte Ihre Festplatte formatieren, könnte Dämonen haben, die aus der Nase des Benutzers fliegen ...Antworten:
Referenzielle Transparenz, auf eine Funktion bezogen, gibt an, dass Sie das Ergebnis der Anwendung dieser Funktion nur anhand der Werte ihrer Argumente bestimmen können. Sie können referenziell transparente Funktionen in jeder Programmiersprache schreiben, z. B. Python, Scheme, Pascal, C.
Andererseits können Sie in den meisten Sprachen auch nicht referenziell transparente Funktionen schreiben. Zum Beispiel diese Python-Funktion:
ist nicht referenziell transparent, in der Tat aufrufen
und
erzeugt für jedes Argument unterschiedliche Werte
x
. Der Grund dafür ist, dass die Funktion eine globale Variable verwendet und ändert. Daher hängt das Ergebnis jedes Aufrufs von diesem sich ändernden Zustand ab und nicht nur vom Argument der Funktion.Haskell, eine rein funktionale Sprache, trennt die Ausdrucksbewertung, in der reine Funktionen angewendet werden und die immer referenziell transparent ist , strikt von der Aktionsausführung (Verarbeitung von Sonderwerten), die nicht referenziell transparent ist, dh die jeweils gleiche Aktion ausführen kann anderes Ergebnis.
Also für jede Haskell-Funktion
und jede ganze Zahl
x
, es ist immer wahr, dassEin Beispiel für eine Aktion ist das Ergebnis der Bibliotheksfunktion
getLine
:Diese Funktion (eigentlich eine Konstante) liefert als Ergebnis der Ausdrucksauswertung zunächst einen reinen Wert vom Typ
IO String
. Werte dieses Typs sind Werte wie alle anderen: Sie können sie weitergeben, in Datenstrukturen einfügen, mit speziellen Funktionen zusammenstellen usw. Zum Beispiel können Sie eine Liste von Aktionen wie folgt erstellen:Aktionen sind insofern besonders, als Sie der Haskell-Laufzeit anweisen können, sie auszuführen, indem Sie Folgendes schreiben:
In diesem Fall durchläuft die Laufzeit beim Starten Ihres Haskell-Programms die daran gebundene Aktion
main
und führt sie aus, wobei möglicherweise Nebenwirkungen auftreten. Daher ist die Aktionsausführung nicht referenziell transparent, da die zweimalige Ausführung derselben Aktion je nach dem, was die Laufzeit als Eingabe erhält, zu unterschiedlichen Ergebnissen führen kann.Dank des Typsystems von Haskell kann eine Aktion niemals in einem Kontext verwendet werden, in dem ein anderer Typ erwartet wird, und umgekehrt. Wenn Sie also die Länge eines Strings ermitteln möchten, können Sie die folgende
length
Funktion verwenden:gibt 5 zurück. Wenn Sie jedoch die Länge eines vom Terminal gelesenen Strings ermitteln möchten, können Sie nicht schreiben
weil Sie einen Typfehler erhalten:
length
Erwartet eine Eingabe von Typ Liste (und ein String ist in der Tat eine Liste),getLine
ist aber ein Wert von TypIO String
(eine Aktion). Auf diese Weise stellt das Typsystem sicher, dass ein Aktionswert wiegetLine
(dessen Ausführung außerhalb der Kernsprache erfolgt und der möglicherweise nicht referenziell transparent ist) nicht in einem Nicht-Aktionswert vom Typ verborgen werden kannInt
.BEARBEITEN
Um eine exakte Frage zu beantworten, ist hier ein kleines Haskell-Programm, das eine Zeile von der Konsole liest und ihre Länge ausgibt.
Die Hauptaktion besteht aus zwei Unteraktionen, die nacheinander ausgeführt werden:
getline
vom TypIO String
,putStrLn
des TypsString -> IO ()
in ihrem Argument ausgewertet wird .Genauer gesagt wird die zweite Aktion von erstellt
line
an den von der ersten Aktion gelesenen Wert,length
(Länge als ganze Zahl berechnen) und dannshow
(die ganze Zahl in eine Zeichenkette verwandeln),putStrLn
auf das Ergebnis vonshow
.An diesem Punkt kann die zweite Aktion ausgeführt werden. Wenn Sie "Hallo" eingegeben haben, wird "5" ausgegeben.
Beachten Sie, dass Sie, wenn Sie einen Wert aus einer Aktion mit der
<-
Notation erhalten, diesen Wert nur in einer anderen Aktion verwenden können, z. B. können Sie nicht schreiben:weil
show (length line)
hat Typ,String
wohingegen die do-Notation erfordert, dass einer Aktion (getLine
vom TypIO String
) eine andere Aktion (z . B.putStrLn (show (length line))
vom TypIO ()
) folgt .BEARBEITEN 2
Jörg W. Mittag definiert referentielle Transparenz allgemeiner als ich (ich habe seine Antwort positiv bewertet). Ich habe eine eingeschränkte Definition verwendet, da sich das Beispiel in der Frage auf den Rückgabewert von Funktionen konzentriert und ich diesen Aspekt veranschaulichen wollte. RT bezieht sich jedoch im Allgemeinen auf die Bedeutung des gesamten Programms, einschließlich Änderungen des globalen Status und Interaktionen mit der Umgebung (IO), die durch die Auswertung eines Ausdrucks verursacht werden. Um eine korrekte, allgemeine Definition zu erhalten, sollten Sie sich auf diese Antwort beziehen.
quelle
IO
Typ in jeder Sprache mit Lambdas und Generika ziemlich einfach implementieren , aber da jederprintln
direkt anrufen kann,IO
garantiert die Implementierung keine Reinheit. Es wäre nur eine Konvention.getLine
nicht referenziell transparent sind, ist falsch. Sie präsentieren,getLine
als würde es zu einer Zeichenfolge ausgewertet oder auf diese reduziert, wobei die jeweilige Zeichenfolge von der Eingabe des Benutzers abhängt. Das ist falsch.IO String
enthält nicht mehr einen String alsMaybe String
.IO String
ist ein Rezept dafür, vielleicht einen String zu erhalten und als Ausdruck so rein wie jeder andere in Haskell.Dies ist jedoch nicht das, was referenzielle Transparenz bedeutet. RT bedeutet, dass Sie jeden Ausdruck im Programm durch das Ergebnis der Auswertung dieses Ausdrucks ersetzen können (oder umgekehrt), ohne die Bedeutung des Programms zu ändern.
Nehmen Sie zum Beispiel das folgende Programm:
Dieses Programm ist referenziell transparent. Ich kann eines oder beide Vorkommen von
f()
durch ersetzen2
und es wird immer noch dasselbe funktionieren:oder
oder
werden sich alle gleich verhalten.
Nun, eigentlich habe ich betrogen. Ich sollte in der Lage sein, den Aufruf von
print
durch seinen Rückgabewert (der überhaupt kein Wert ist) zu ersetzen, ohne die Bedeutung des Programms zu ändern. Wenn ich jedoch nur die beidenprint
Anweisungen entferne , ändert sich die Bedeutung des Programms: Vorher druckte es etwas auf den Bildschirm, danach nicht. E / A ist nicht referenziell transparent.Die einfache Faustregel lautet: Wenn Sie einen beliebigen Ausdruck, Unterausdruck oder Unterprogrammaufruf durch den Rückgabewert dieses Ausdrucks, Unterausdrucks oder Unterprogrammaufrufs an einer beliebigen Stelle im Programm ersetzen können, ohne dass das Programm seine Bedeutung ändert, haben Sie einen Verweis Transparenz. In der Praxis bedeutet dies, dass Sie keine E / A haben, keinen veränderlichen Zustand haben und keine Nebenwirkungen haben können. In jedem Ausdruck darf der Wert des Ausdrucks nur von den Werten der Bestandteile des Ausdrucks abhängen. Und in jedem Unterprogrammaufruf darf der Rückgabewert nur von den Argumenten abhängen.
quelle
print
Beispiel wirklich . Vielleicht ist eine Möglichkeit, dies zu sehen, dass das, was auf dem Bildschirm gedruckt wird, Teil des "Rückgabewerts" ist. Wenn Sieprint
mit seiner Funktion den Rückgabewert und die entsprechende Schreibweise auf dem Terminal ersetzen können, funktioniert das Beispiel.4
und2 + 2
nicht austauschbar sein, da sie unterschiedliche Laufzeiten haben, und der springende Punkt der referenziellen Transparenz ist, dass Sie einen Ausdruck durch das ersetzen können, wonach er ausgewertet wird. Die wichtige Überlegung wäre die Fadensicherheit.listOfSequence.append(n)
kehrt zurückNone
, so dass Sie in der Lage sein sollten, jeden Aufruf vonlistOfSequence.append(n)
durch zu ersetzen,None
ohne die Bedeutung Ihres Programms zu ändern. Können Sie das tun? Wenn nicht, ist es nicht referenziell transparent.Teile dieser Antwort stammen direkt aus einem unvollendeten Tutorial zur funktionalen Programmierung , das auf meinem GitHub-Account gehostet wird:
Betrachten Sie ein einfaches Beispiel:
In einer reinen funktionalen Sprache sind die linke und die rechte Seite des Gleichheitszeichens in beide Richtungen austauschbar. Das heißt, anders als in einer Sprache wie C behauptet die obige Notation wirklich eine Gleichheit. Dies hat zur Folge, dass wir über Programmcode wie über mathematische Gleichungen nachdenken können.
Aus dem Haskell-Wiki :
Im Gegensatz dazu wird die Art der von C-ähnlichen Sprachen ausgeführten Operation manchmal als destruktive Zuweisung bezeichnet .
Der Begriff pure wird häufig verwendet, um eine für diese Diskussion relevante Eigenschaft von Ausdrücken zu beschreiben. Damit eine Funktion als rein betrachtet wird,
Gemäß der Black-Box-Metapher, die in zahlreichen mathematischen Lehrbüchern zu finden ist, sind die Interna einer Funktion vollständig von der Außenwelt abgeschlossen. Ein Nebeneffekt ist, wenn eine Funktion oder ein Ausdruck gegen dieses Prinzip verstößt - das heißt, die Prozedur darf auf irgendeine Weise mit anderen Programmeinheiten kommunizieren (z. B. um Informationen auszutauschen und auszutauschen).
Zusammenfassend ist die referenzielle Transparenz ein Muss, damit sich Funktionen wie echte mathematische Funktionen verhalten , auch in der Semantik von Programmiersprachen.
quelle
[here](link to source)
...", gefolgt von 2) der korrekten Formatierung des Zitats (verwenden Sie Anführungszeichen oder besser noch ein>
Symbol dafür). Es würde auch nicht schaden, wenn Sie nicht nur eine allgemeine Anleitung geben, sondern auch konkrete Fragen beantworten, in diesem Fall überf(x)+f(x)
/2*f(x)
, siehe Wie man antwortet - ansonsten könnte es so aussehen, als würden Sie einfach für Ihre Seite werben