Frage

F # Wikibook unter Einkapseln Mutable Staates Abschnitts gibt es einen folgenden Code-Schnipsel .

> let incr =
    let counter = ref 0
    fun () ->
        counter := !counter + 1
        !counter;;

val incr : (unit -> int)

> incr();;
val it : int = 1

> incr();;
val it : int = 2

> incr();;
val it : int = 3

Zunächst schien es einfach genug, um die Tatsache zu schlucken, dass wandelbar counter Wert erhöht jedes Mal incr aufgerufen wird.

Aber nach für eine Weile darüber nachzudenken, was ich konnte nicht verstehen, wenn waren counter von Haufen befreit wird und auch, wie counter bezieht sich noch auf dem vorherigen Wert vor erhöht wird. Wie wird counter, die innerhalb incr Funktionsumfang lebt überleben durch mehrere Funktionsaufrufe?

So wichtigsten Fragen sind:

  • Wann wird counter befreit von Heap?
  • Ist das nicht counter ein Speicherleck?
War es hilfreich?

Lösung

Die Unterscheidung zwischen ‚lexikalischen Gültigkeitsbereich‘ und ‚Lebensdauer‘ (Laufzeitdauer zwischen, wenn das Objekt erstellt wird und zerstört) (wobei ein Name in den Text eines Programms Bedeutung) kann manchmal verwirrend sein, da oft sind diese beiden stark korreliert . Doch die durch dieses Beispiel gezeigt Technik ist üblich, in funktionalen Sprachen: Sie einer Implementierung Detail einen kleinen lexikalischen Gültigkeitsbereich geben (das versteckt die Details der Implementierung von Anrufern), aber ihre Lebensdauer verlängern, indem sie in einem Verschluss erfassen (so dass ihre Lebensdauer die Lebensdauer wird des einschließenden Objekts - in diesem Fall die ‚incr‘ -Funktion). Dies ist eine übliche Art und Weise Verkapselung in der funktionalen Programmierung zu tun (mit der üblichen Verkapselungsverfahren von öffentlichen / privaten in den Klassen in der objektorientierten Programmierung kontrastierte).

Nun, in diesem Beispiel sieht es aus wie ‚incr‘ ist eine Top-Level-Funktion, was bedeutet, sein Wert für die Laufzeit des Programms dauert (oder interaktive Sitzung, wenn sie in fsi.exe eingeben). Man könnte dies ein ‚Leck‘ nennen, aber es hängt von der Absicht. Wenn Sie einige eindeutige ID-Zähler haben Sie für die gesamte Lebensdauer des gesamten Programms benötigen, dann sind Sie gehen zu müssen, daß der Zähler varable speichern irgendwo , dass es für das gesamte Programm dauert. Also entweder ist dies ‚ein Leck‘ oder ‚ein von Design-Merkmale‘ je nachdem, wie ‚incr‘ verwendet wird (die Sie benötigen diese Funktion für den gesamten Rest des Programms verwenden?). Auf jeden Fall hier der entscheidende Punkt ist, dass ‚incr‘ Speicherressourcen hält, wenn Sie also nicht für immer diese Ressourcen benötigen, sollten Sie sich für die Schließung von ‚incr‘ verwiesen ordnen unerreichbar zu werden, wenn es nicht mehr benötigt wird. sein könnte dies häufig durch sie lokal für eine andere Funktion zu machen, z.

let MyComplicatedFuncThatNeedsALocalCounter args =
    let incr = 
        // as before
    // other code that uses incr
    // return some result that does not capture incr

Andere Tipps

In diesem Fall ist incr eine Top-Level-Funktion (als statisches Feld umgesetzt, wenn ich mich nicht irre). Es hat einen Verschluss, der wiederum eine Referenz auf diese ref Zelle namens counter hat. Solange dieser Verschluss vorhanden ist, wird die ref Zelle im Speicher gehalten.

Nun ist diese Top-Level-Bindung wird in der Tat nie Müll gesammelt bekommen, wie es ein statisches Nur-Lese-Feld ist. (In C # Bedingungen). Wenn Sie jedoch haben Verschlüsse wie die mit einem beschränkt Lebensdauer (lokal oder in einem Objekt gebunden ist), wird die ref Zelle befreit werden, wenn der Verschluss Müll gesammelt wird.

Zähler wird aus dem Heap freigegeben, wenn incr nicht mehr erreichbar ist. Es ist nicht ein Speicherverlust wegen der Garbage Collection.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top