Weitere Erläuterungen zur Lexikalischen Bindung in Closures?

8

Es gibt viele SO-Posts, die damit in Zusammenhang stehen, aber ich frage das erneut mit einem anderen Zweck

Ich versuche zu verstehen, warum Schließungen wichtig und nützlich sind. Eines der Dinge, die ich in anderen SO-Posts gelesen habe, ist, dass wenn Sie eine Variable an Closure übergeben, der Closure ab diesem Zeitpunkt an diesen Wert erinnert. Ist das der ganze technische Aspekt davon oder gibt es mehr, was dort passiert.

Was ich mich dann wundere ist, was passieren würde, wenn die Variable, die innerhalb des Verschlusses verwendet wird, von außen verändert wird. Sollten sie nur Konstanten sein?

In der Sprache Clojure kann ich Folgendes tun: Aber da Werte unveränderlich sind, tritt dieses Problem nicht auf. Was ist mit anderen Sprachen und was ist die richtige technische Definition einer Schließung?

%Vor%     
user855 15.12.2009, 18:43
quelle

8 Antworten

9

Dies ist nicht die Art von Antwort, die hier aufsteht, aber ich möchte Sie herzlich dazu auffordern, die Antwort auf Ihre Frage zu finden, indem Sie Shriram Krishnamurthis (gratis!) (online!) Lehrbuch Ich werde das Buch sehr, sehr kurz umschreiben, indem ich die Entwicklung der klitzekleinen Dolmetscher zusammenfasse, durch die es dich führt:

  • eine arithmetische Ausdruckssprache (AE)
  • eine arithmetische Ausdruckssprache mit benannten Ausdrücken (WAE); Um dies zu implementieren, müssen Sie eine Substitution Funktion entwickeln, die dies kann Ersetzen Sie Namen durch Werte
  • eine Sprache, die Funktionen erster Ordnung hinzufügt (F1WAE): Die Verwendung einer Funktion beinhaltet Ersetzen Werte für jeden der Parameternamen.
  • Die gleiche Sprache, ohne Ersetzung: Es stellt sich heraus, dass "Umgebungen" Ihnen erlauben, den Overhead der vorbeugenden Substitution zu vermeiden.
  • eine Sprache, die die Trennung zwischen Funktionen und Ausdrücken durch Zulassen eliminiert Funktionen, die an beliebigen Stellen definiert werden sollen (FWAE)

Das ist der entscheidende Punkt: Sie implementieren dies, und dann entdecken Sie, dass es mit Substitution funktioniert, aber mit Umgebungen ist es kaputt. Um dies zu beheben, müssen Sie insbesondere sicherstellen, dass Sie der Umgebung, die bei der Auswertung vorhanden war, eine evaluierte Funktionsdefinition zuordnen. Dieses Paar (fundef + environment-of-definition) wird als "closure" bezeichnet.

Puh!

Okay, was passiert, wenn wir dem Bild veränderbare Bindungen hinzufügen? Wenn Sie dies selbst ausprobieren, werden Sie feststellen, dass die natürliche Implementierung eine Umgebung ersetzt, die Namen mit Werten in einer Umgebung verknüpft, die Namen Bindungen zuordnet. Dies ist orthogonal zum Begriff der Schließungen; Da Closures Umgebungen erfassen und Umgebungen jetzt Bindungen zuordnen, erhalten Sie das von Ihnen beschriebene Verhalten, wobei die Mutation einer in einer Umgebung erfassten Variablen sichtbar und beständig ist.

Noch einmal, ich möchte Sie dringend bitten, sich PLAI anzusehen.

    
John Clements 17.12.2009 03:33
quelle
5

Eine Closure ist eine Datenstruktur, die vom Compiler verwendet wird, um sicherzustellen, dass eine Funktion immer Zugriff auf die Daten hat, die sie benötigen. Hier ist ein Beispiel für eine Funktion, die bei der Definition aufzeichnet.

%Vor%

Wenn diese Funktion definiert ist, wird eine Klasse erstellt und eine kleine Struktur (eine Closure) wird auf dem Heap zugewiesen, der den Wert von foo speichert. Die Klasse hat einen Zeiger darauf (oder es enthält es nicht sicher). Wenn Sie dies erneut ausführen, würde eine zweite Schließung zugeordnet werden, um diese andere foo zu halten. Wenn wir sagen "diese Funktion schließt über foo", meinen wir damit, dass es einen Verweis auf eine Striktur / Klasse / was auch immer hat, speichert den Zustand von foo zum Zeitpunkt der Erstellung. Der Grund, warum Sie etwas schließen müssen, ist, dass die Funktion, die es enthält, verschwindet, bevor die Daten verwendet werden. In diesem Fall wird das äußere (welches den Wert von foo enthält) enden und lange vor der Verwendung von foo verschwinden, so dass niemand mehr in der Lage sein wird, foo zu modifizieren. natürlich könnte foo jemandem einen ref einreichen, der es dann modifizieren könnte.

    
Arthur Ulfeldt 15.12.2009 19:11
quelle
5

Ein lexikalischer Abschluss ist einer, in dem die eingeschlossenen Variablen (z. B. greeting-prefix in Ihrem Beispiel) durch Verweis eingeschlossen sind. Der erstellte Abschluss erhält nicht einfach den Wert von greeting-prefix zum Zeitpunkt der Erstellung, sondern erhält eine Referenz. Wenn greeting-prefix nach dem Erstellen der Closure geändert wird, wird der neue Wert von der Closure bei jedem Aufruf verwendet.

In reinen funktionalen Sprachen ist das kein großer Unterschied, denn Werte werden nie geändert. Es spielt also keine Rolle, ob der Wert von greeting-prefix in die Closure kopiert wird: Es gibt keinen möglichen Unterschied im Verhalten, der sich aus der Bezugnahme auf das Original gegenüber seiner Kopie ergeben könnte.

In "Imperativ-Sprachen-mit-Schließungen", wie C # und Java (über anonyme Klassen), muss eine Entscheidung darüber getroffen werden, ob die eingeschlossene Variable durch Wert oder durch Verweis eingeschlossen ist. In Java wird dieser Entscheidung vorgebeugt, indem nur final -Variablen eingeschlossen werden, was eine funktionale Sprache nachahmt, die diese Variable betrifft. In C # glaube ich, dass es eine andere Sache ist.

Das Einschließen nach Wert vereinfacht die Implementierung: Die einzuschließende Variable ist oft auf dem Stapel vorhanden und wird daher zerstört, wenn die Funktion, die die Schließung konstruiert, zurückkehrt - das heißt, sie kann nicht durch Referenz eingeschlossen werden. Wenn Sie einen Enclosure-Verweis benötigen, können Sie solche Variablen identifizieren und sie bei jedem Aufruf dieser Funktion in einem Objekt reservieren. Dieses Objekt wird dann als Teil der Umgebung des Verschlusses aufbewahrt und muss so lange in Betrieb bleiben, wie alle Verschlüsse, die es verwenden, aktiv sind. (Ich weiß nicht, ob kompilierte Sprachen diese Technik direkt verwenden.)

    
Edmund 16.12.2009 06:09
quelle
2

Sie können sich eine Schließung als "Umgebung" vorstellen, in der Namen an Werte gebunden sind. Diese Namen sind für die Schließung völlig privat, weshalb wir sagen, dass sie ihre Umgebung "schließt". Ihre Frage ist also nicht sinnvoll, da das "Äußere" die geschlossene Umgebung nicht beeinflussen kann. Ja, eine Schließung kann sich auf einen Namen in einer globalen Umgebung beziehen (mit anderen Worten, wenn ein Name verwendet wird, der nicht in seiner privaten, geschlossenen Umgebung gebunden ist), aber das ist eine andere Geschichte.

Wenn Sie möchten, können Sie sich eine Umgebung als Wörterbuch oder Hash-Tabelle vorstellen. Eine Schließung erhält ein eigenes kleines Wörterbuch, in dem Namen nachgeschlagen werden.

    
Jonathan Feinberg 15.12.2009 18:52
quelle
2

Sie könnten gerne lesen Auf Lambda, Capture und Mutabilität , die beschreibt, wie das funktioniert in C # und F # zum Vergleich.

    
Brian 16.12.2009 06:14
quelle
1

Sehen Sie sich diesen Blogbeitrag an: ADTs in Clojure . Es zeigt eine schöne Anwendung von Closures auf das Problem der Datensperrung, so dass es ausschließlich über eine bestimmte Schnittstelle zugänglich ist (Rendering des Datentyps undurchsichtig).

Der Grundgedanke hinter dieser Art der Sperrung ist einfacher mit dem Gegenbeispiel illustriert, das huaiyuan in Common Lisp gepostet hat, als ich diese Antwort verfasste. Tatsächlich ist die Clojure-Version insofern interessant, als sie zeigt, dass das Problem einer geschlossenen Variable, die ihren Wert ändert, in Clojure auftaucht, wenn die Variable zufällig eine Instanz eines der Referenztypen enthält.

%Vor%

Wie beim ursprünglichen Make-Greeter-Beispiel könnten Sie es so umschreiben (beachten Sie den deref / @):

%Vor%

Dann können Sie damit personalisierte Grüße von den verschiedenen Betreibern verschiedener Bereiche einer Website rendern. : -)

%Vor%     
Michał Marczyk 16.12.2009 09:06
quelle
0
  

Sie können sich eine Schließung als eine denken   "Umgebung", in der Namen sind   an Werte gebunden. Diese Namen sind   ganz privat für die Schließung, die   Deshalb sagen wir, dass es sich "schließt"   seine Umgebung. Also deine Frage   ist nicht sinnvoll, dass die   "draußen" kann nicht beeinflussen   geschlossene Umgebung. Ja ein   Schließung kann sich auf einen Namen in a beziehen   globale Umgebung (mit anderen Worten, wenn   Es verwendet einen Namen, der nicht gebunden ist   seine private, geschlossene Umgebung),   aber das ist eine andere Geschichte.

Ich vermute, dass die Frage war, ob solche Dinge in Sprachen möglich sind, die eine Mutation lokaler Variablen erlauben:

%Vor%

Und - da sie offensichtlich möglich sind - denke ich, dass die Frage legitim ist.

    
danlei 12.02.2010 18:02
quelle