scala - titel - title tag länge 2018




Wie kann eine Zeitfunktion in der funktionalen Programmierung existieren? (9)

Wenn ja, wie kann es dann existieren? Verstößt es nicht gegen das Prinzip der funktionalen Programmierung? Es verletzt insbesondere die referentielle Transparenz

Es existiert nicht in einem rein funktionalen Sinn.

Oder wenn nein, wie kann man dann die aktuelle Zeit in der funktionalen Programmierung kennen?

Es kann zunächst nützlich sein zu wissen, wie eine Zeit auf einem Computer abgerufen wird. Im Wesentlichen gibt es Onboard-Schaltungen, die die Zeit verfolgen (weshalb ein Computer normalerweise eine kleine Batterie benötigt). Dann kann es einen internen Prozess geben, der den Wert der Zeit in einem bestimmten Speicherregister festlegt. Was im Wesentlichen auf einen Wert hinausläuft, der von der CPU abgerufen werden kann.

Für Haskell gibt es ein Konzept einer "IO-Aktion", die einen Typ repräsentiert, der dazu gebracht werden kann, einen IO-Prozess auszuführen. Anstatt einen Zeitwert zu referenzieren, referenzieren wir einen IO Time Wert. All das wäre rein funktional. Wir beziehen uns nicht auf die time sondern auf etwas wie "lese den Wert des Zeitregisters" .

Wenn wir das Haskell-Programm tatsächlich ausführen, würde die IO-Aktion tatsächlich stattfinden.

Ich muss zugeben, dass ich nicht viel über funktionale Programmierung weiß. Ich lese darüber von hier und da und weiß, dass bei funktionaler Programmierung eine Funktion dieselbe Ausgabe für dieselbe Eingabe zurückgibt, egal wie oft die Funktion aufgerufen wird. Es ist genau wie eine mathematische Funktion, die dieselbe Ausgabe für den gleichen Wert des Eingabeparameters auswertet, der im Funktionsausdruck enthalten ist.

Betrachten Sie zum Beispiel Folgendes:

f(x,y) = x*x + y; //it is a mathematical function

Egal wie oft Sie f(10,4) , sein Wert ist immer 104 . Wenn Sie also f(10,4) schreiben, können Sie es durch 104 ersetzen, ohne den Wert des gesamten Ausdrucks zu ändern. Diese Eigenschaft wird als referenzielle Transparenz eines Ausdrucks bezeichnet.

Wie Wikipedia sagt ( link ),

Umgekehrt hängt im Funktionscode der Ausgabewert einer Funktion nur von den Argumenten ab, die in die Funktion eingegeben werden. Wenn also eine Funktion f zweimal mit demselben Wert für ein Argument x aufgerufen wird, wird beide Male das gleiche Ergebnis f (x) erzeugt.

Meine Frage ist also: Kann eine Zeitfunktion (die die aktuelle Zeit zurückgibt) in der funktionalen Programmierung existieren?

  • Wenn ja, wie kann es dann existieren? Verstößt es nicht gegen das Prinzip der funktionalen Programmierung? Es verletzt insbesondere die referentielle Transparenz, die eine Eigenschaft der funktionalen Programmierung ist (wenn ich sie richtig verstehe).

  • Oder wenn nein, wie kann man dann die aktuelle Zeit in der funktionalen Programmierung kennen?


"Aktuelle Zeit" ist keine Funktion. Es ist ein Parameter. Wenn Ihr Code von der aktuellen Uhrzeit abhängt, bedeutet dies, dass Ihr Code nach der Zeit parametriert ist.


Eine andere Möglichkeit, dies zu erklären, ist dies: Keine Funktion kann die aktuelle Zeit erhalten (da sie sich ständig ändert), aber eine Aktion kann die aktuelle Zeit erhalten. getClockTime , getClockTime ist eine Konstante (oder eine Nullfunktion, wenn Sie möchten), die die Aktion darstellt , die aktuelle Zeit zu erhalten. Diese Aktion ist jedes Mal gleich, egal wann sie verwendet wird, also ist sie eine echte Konstante.

Nehmen wir einmal an, dass das print eine Funktion ist, die eine gewisse Zeitdarstellung benötigt und sie auf der Konsole ausgibt. Da Funktionsaufrufe in der reinen funktionalen Sprache keine Nebeneffekte haben können, stellen wir uns stattdessen vor, dass es sich um eine Funktion handelt, die einen Zeitstempel benötigt und die Aktion zum Drucken an die Konsole zurückgibt. Auch dies ist eine echte Funktion, denn wenn Sie ihm denselben Zeitstempel geben, wird er jedes Mal dieselbe Aktion ausgeben.

Wie können Sie jetzt die aktuelle Uhrzeit auf der Konsole ausgeben? Nun, Sie müssen die zwei Aktionen kombinieren. Wie können wir das machen? Wir können getClockTime nicht einfach zum print , da print einen Zeitstempel und keine Aktion erwartet. Aber wir können uns vorstellen, dass es einen Operator gibt, >>= , der zwei Aktionen kombiniert , eine, die einen Zeitstempel erhält, und eine, die einen als Argument nimmt und ausdruckt. Wenn Sie dies auf die zuvor erwähnten Aktionen anwenden, ist das Ergebnis ... tadaaa ... eine neue Aktion, die die aktuelle Zeit erhält und sie druckt. Und genau so wird es in Haskell gemacht.

Prelude> System.Time.getClockTime >>= print
Fri Sep  2 01:13:23 東京 (標準時) 2011

Man kann es also konzeptionell so sehen: Ein reines Funktionsprogramm führt kein IO durch, es definiert eine Aktion , die das Laufzeitsystem dann ausführt. Die Aktion ist jedes Mal gleich, aber das Ergebnis ihrer Ausführung hängt von den Umständen ab, zu denen sie ausgeführt wird.

Ich weiß nicht, ob das klarer war als die anderen Erklärungen, aber manchmal hilft es mir, so darüber nachzudenken.


Es kann absolut rein funktional gemacht werden. Es gibt mehrere Möglichkeiten dies zu tun, aber die einfachste ist, dass die Zeitfunktion nicht nur die Zeit zurückgibt, sondern auch die Funktion, die Sie aufrufen müssen, um die nächste Zeitmessung zu erhalten .

In C # könnte man es so implementieren:

// Exposes mutable time as immutable time (poorly, to illustrate by example)
// Although the insides are mutable, the exposed surface is immutable.
public class ClockStamp {
    public static readonly ClockStamp ProgramStartTime = new ClockStamp();
    public readonly DateTime Time;
    private ClockStamp _next;

    private ClockStamp() {
        this.Time = DateTime.Now;
    }
    public ClockStamp NextMeasurement() {
        if (this._next == null) this._next = new ClockStamp();
        return this._next;
    }
}

(Beachten Sie, dass dies ein Beispiel ist, das einfach und nicht praktikabel sein soll. Insbesondere können die Listenknoten nicht mit Garbage Collection erfasst werden, da sie von ProgramStartTime gerootet werden.)

Diese 'ClockStamp'-Klasse verhält sich wie eine unveränderbare verkettete Liste, aber in Wirklichkeit werden die Knoten auf Anforderung erzeugt, so dass sie die' aktuelle 'Zeit enthalten können. Jede Funktion, die die Zeit messen möchte, sollte einen "clockStamp" -Parameter haben und ihre letzte Zeitmessung in ihrem Ergebnis zurückgeben (so dass der Aufrufer keine alten Messungen sieht), wie folgt:

// Immutable. A result accompanied by a clockstamp
public struct TimeStampedValue<T> {
    public readonly ClockStamp Time;
    public readonly T Value;
    public TimeStampedValue(ClockStamp time, T value) {
        this.Time = time;
        this.Value = value;
    }
}

// Times an empty loop.
public static TimeStampedValue<TimeSpan> TimeALoop(ClockStamp lastMeasurement) {
    var start = lastMeasurement.NextMeasurement();
    for (var i = 0; i < 10000000; i++) {
    }
    var end = start.NextMeasurement();
    var duration = end.Time - start.Time;
    return new TimeStampedValue<TimeSpan>(end, duration);
}

public static void Main(String[] args) {
    var clock = ClockStamp.ProgramStartTime;
    var r = TimeALoop(clock);
    var duration = r.Value; //the result
    clock = r.Time; //must now use returned clock, to avoid seeing old measurements
}

Natürlich ist es etwas unpraktisch, die letzte Messung ein- und auszugeben, hinein und heraus, rein und raus. Es gibt viele Möglichkeiten, den Textbaustein zu verbergen, insbesondere auf der Ebene des Sprachdesigns. Ich denke, Haskell benutzt diesen Trick und versteckt dann die hässlichen Teile mit Monaden.


Ihre Frage verbindet zwei verwandte Maße einer Computersprache: funktional / imperativ und rein / unrein.

Eine funktionale Sprache definiert Beziehungen zwischen Eingaben und Ausgaben von Funktionen, und eine imperative Sprache beschreibt bestimmte Vorgänge in einer bestimmten Reihenfolge.

Eine reine Sprache erzeugt oder hängt nicht von Nebenwirkungen ab, und eine unreine Sprache verwendet sie durchgängig.

Einhundert Prozent reine Programme sind grundsätzlich nutzlos. Sie können eine interessante Berechnung durchführen, aber weil sie keine Nebenwirkungen haben können, haben sie keine Eingabe oder Ausgabe, so dass Sie nie wissen würden, was sie berechnet haben.

Um überhaupt nützlich zu sein, muss ein Programm zumindest unrein sein. Eine Möglichkeit, ein reines Programm nützlich zu machen, besteht darin, es in einen dünnen unreinen Wrapper zu stecken. Wie dieses ungeprüfte Haskell-Programm:

-- this is a pure function, written in functional style.
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

-- This is an impure wrapper around the pure function, written in imperative style
-- It depends on inputs and produces outputs.
main = do
    putStrLn "Please enter the input parameter"
    inputStr <- readLine
    putStrLn "Starting time:"
    getCurrentTime >>= print
    let inputInt = read inputStr    -- this line is pure
    let result = fib inputInt       -- this is also pure
    putStrLn "Result:"
    print result
    putStrLn "Ending time:"
    getCurrentTime >>= print

In Haskell verwendet man ein Konstrukt namens Monade , um Nebenwirkungen zu behandeln. Eine Monade bedeutet im Grunde, dass Sie Werte in einen Container kapseln und einige Funktionen haben, um Funktionen von Werten zu Werten innerhalb eines Containers zu verketten. Wenn unser Container den Typ hat:

data IO a = IO (RealWorld -> (a,RealWorld))

Wir können IO-Aktionen sicher implementieren. Dieser Typ bedeutet: Eine Aktion vom Typ IO ist eine Funktion, die ein Token vom Typ RealWorld und ein neues Token zusammen mit einem Ergebnis zurückgibt.

Die Idee dahinter ist, dass jede IO Aktion den äußeren Zustand mutiert, repräsentiert durch das magische Token RealWorld . Mit Monaden kann man mehrere Funktionen verketten, die die reale Welt zusammen verändern. Die wichtigste Funktion einer Monade ist >>= , ausgesprochen bind :

(>>=) :: IO a -> (a -> IO b) -> IO b

>>= nimmt eine Aktion und eine Funktion, die das Ergebnis dieser Aktion übernimmt und daraus eine neue Aktion erstellt. Der Rückgabetyp ist die neue Aktion. Nehmen wir zum Beispiel an, dass es now :: IO String eine Funktion gibt now :: IO String , die einen String zurückgibt, der die aktuelle Zeit darstellt. Wir können es mit der Funktion putStrLn , um es auszudrucken:

now >>= putStrLn

Oder in do -Notation geschrieben, was einem imperativen Programmierer geläufiger ist:

do currTime <- now
   putStrLn currTime

All dies ist rein, da wir die Mutation und Informationen über die Außenwelt auf den RealWorld Token RealWorld . Jedes Mal, wenn Sie diese Aktion ausführen, erhalten Sie natürlich eine andere Ausgabe, aber die Eingabe ist nicht die gleiche: Der RealWorld Token ist anders.


Ja! Du hast Recht! Now () oder CurrentTime () oder jede Methodensignatur eines solchen Flavors zeigt keine referentielle Transparenz in einer Weise. Aber durch Anweisung an den Compiler wird es durch eine Systemtakteingabe parametrisiert.

Bei der Ausgabe sieht Now () so aus, als ob der referenziellen Transparenz nicht gefolgt wird. Aber das tatsächliche Verhalten der Systemuhr und der darüber liegenden Funktion entspricht der referentiellen Transparenz.


Ja, die Zeitfunktion kann in FP mit einer leicht modifizierten Version auf FP, die als unreine FP bekannt ist, existieren (die Standardversion oder die Hauptversion ist reine FP).

Im Falle, dass man die Zeit braucht (oder die Datei liest oder eine Rakete startet), muss der Code mit der Außenwelt interagieren, um die Arbeit zu erledigen, und diese Außenwelt basiert nicht auf reinen Grundlagen von FP. Um einer reinen FP-Welt die Interaktion mit dieser unreinen Außenwelt zu ermöglichen, haben die Menschen unreine FP eingeführt. Schließlich ist eine Software, die nicht mit der Außenwelt interagiert, nichts anderes als einige mathematische Berechnungen.

Einige FP-Programmiersprachen haben diese Unreinheitsfunktion eingebaut, so dass es nicht einfach ist, herauszufinden, welcher Code unrein ist und welcher (wie F # etc) rein ist und einige FP-Sprachen sicherstellen, dass wenn man unreines Zeug tut, dieser Code eindeutig ist im Vergleich zu reinem Code, wie Haskell.

Eine andere interessante Möglichkeit, dies zu sehen, wäre, dass Ihre Zeitfunktion in FP ein "Welt" -Objekt nehmen würde, das den gegenwärtigen Zustand der Welt wie Zeit, Anzahl der Menschen in der Welt etc. hat Sei immer rein, dh du passierst im selben Weltzustand du wirst immer die gleiche Zeit bekommen.


Sie sprechen ein sehr wichtiges Thema in der funktionalen Programmierung an, das heißt IO durchzuführen. Die Art und Weise, wie viele reine Sprachen es tun, besteht darin, eingebettete domänenspezifische Sprachen zu verwenden, z. B. eine Subsprache, deren Aufgabe es ist, Aktionen zu codieren, die Ergebnisse haben können. Die Haskell-Laufzeit erwartet zum Beispiel, dass ich eine Aktion namens " main , die aus allen Aktionen besteht, aus denen mein Programm besteht. Die Laufzeitumgebung führt diese Aktion aus. Meistens führt es dabei reinen Code aus. Von Zeit zu Zeit verwendet die Laufzeitumgebung die berechneten Daten, um I / O durchzuführen und Daten zurück in reinen Code zu übertragen.

Sie könnten sich beschweren, dass dies wie Betrug klingt, und in gewisser Weise ist es: Indem man Aktionen definiert und erwartet, dass die Laufzeit sie ausführt, kann der Programmierer alles tun, was ein normales Programm tun kann. Aber das starke Typ-System von Haskell schafft eine starke Barriere zwischen reinen und "unreinen" Teilen des Programms: Man kann nicht einfach zwei Sekunden zur aktuellen CPU-Zeit hinzufügen und drucken, man muss eine Aktion definieren, die zur aktuellen führt CPU-Zeit und leiten das Ergebnis an eine andere Aktion weiter, die zwei Sekunden hinzufügt und das Ergebnis ausgibt. Zu viel Programm zu schreiben wird jedoch als schlechter Stil angesehen, weil es im Vergleich zu Haskell-Typen, die uns alles darüber erzählen, was ein Wert ist, schwer zu sagen ist, welche Effekte verursacht werden.

Beispiel: clock_t c = time(NULL); printf("%d\n", c + 2); clock_t c = time(NULL); printf("%d\n", c + 2); in C, gegen main = getCPUTime >>= \c -> print (c + 2*1000*1000*1000*1000) in Haskell. Der Operator >>= wird verwendet, um Aktionen zu erstellen, wobei das Ergebnis der ersten an eine Funktion übergeben wird, die zu der zweiten Aktion führt. Das sieht ziemlich geheimnisvoll aus, Haskell-Compiler unterstützen syntaktischen Zucker, der es uns ermöglicht, den letzteren Code wie folgt zu schreiben:

type Clock = Integer -- To make it more similar to the C code

-- An action that returns nothing, but might do something
main :: IO ()
main = do 
    -- An action that returns an Integer, which we view as CPU Clock values
    c <- getCPUTime :: IO Clock 
    -- An action that prints data, but returns nothing
    print (c + 2*1000*1000*1000*1000) :: IO ()

Letzteres sieht ziemlich zwingend aus, oder?





clean-language