Mit dynamischem Scoping kann ein Angerufener auf die Variablen seines Anrufers zugreifen. Pseudo-C-Code:
void foo()
{
print(x);
}
void bar()
{
int x = 42;
foo();
}
Da ich noch nie in einer Sprache programmiert habe, die dynamisches Scoping unterstützt, frage ich mich, wie einige reale Anwendungsfälle für dynamisches Scoping aussehen würden.
Antworten:
Eine sehr nützliche Anwendung des dynamischen Scoping besteht darin , Kontextparameter zu übergeben, ohne dass jeder Funktion in einem Aufrufstapel explizit neue Parameter hinzugefügt werden müssen
Beispielsweise unterstützt Clojure das dynamische Scoping über das Binden , mit dem der Wert
*out*
für das Drucken vorübergehend neu zugewiesen werden kann . Wenn Sie erneut binden,*out*
wird jeder Aufruf zum Drucken im dynamischen Bereich der Bindung in Ihrem neuen Ausgabestream gedruckt. Sehr nützlich, wenn Sie beispielsweise alle gedruckten Ausgaben in eine Art Debugging-Protokoll umleiten möchten.Beispiel: Im folgenden Code wird die Do-Stuff-Funktion in der Debug-Ausgabe und nicht in der Standardausgabe gedruckt. Beachten Sie jedoch, dass ich keinen Ausgabeparameter zu Do-Stuff hinzufügen musste, um dies zu aktivieren.
Beachten Sie, dass die Bindungen von Clojure auch threadlokal sind, sodass Sie kein Problem mit der gleichzeitigen Verwendung dieser Funktion haben. Dies macht Bindungen erheblich sicherer als (ab) die Verwendung globaler Variablen für denselben Zweck.
quelle
(Haftungsausschluss: Ich habe noch nie in einer dynamischen Scoping-Sprache programmiert.)
Das Scoping ist viel einfacher zu implementieren und möglicherweise schneller. Beim dynamischen Scoping wird nur die eine Symboltabelle benötigt (die derzeit verfügbaren Variablen). Es liest nur aus dieser Symboltabelle für alles.
Stellen Sie sich in Python dieselbe Funktion vor.
Wenn ich bar aufrufe, füge ich x in die Symboltabelle ein. Wenn ich foo aufrufe, nehme ich die aktuell für bar verwendete Symboltabelle und schiebe sie auf den Stapel. Ich rufe dann foo auf, an das x übergeben wurde (wahrscheinlich wurde es beim Aufrufen der Funktion in die neue Symboltabelle eingefügt). Nach dem Beenden der Funktion muss ich den neuen Bereich zerstören und den alten wiederherstellen.
Beim dynamischen Scoping ist dies nicht erforderlich. Ich muss nur die Anweisung kennen, zu der ich zurückkehren muss, wenn die Funktion endet, da nichts an der Symboltabelle getan werden muss.
quelle
Die Ausnahmebehandlung in den meisten Sprachen verwendet dynamisches Scoping. Wenn eine Ausnahme auftritt, wird die Steuerung an den nächstgelegenen Handler im (dynamischen) Aktivierungsstapel zurücküberwiesen.
quelle
return
Anweisungen in den meisten Sprachen dynamisches Scoping verwenden, weil sie die Kontrolle an den Aufrufer auf dem Stapel zurückgeben?Ich bin mir nicht 100% sicher, ob dies eine exakte Übereinstimmung ist, aber ich denke, dass es im Allgemeinen zumindest nahe genug kommt, um zu zeigen, wo es nützlich sein kann, um Regeln für den Geltungsbereich zu brechen oder zu ändern.
Die Ruby-Sprache ist die Vorlagenklasse ERB, die beispielsweise in Rails zum Generieren von HTML-Dateien verwendet wird. Wenn Sie es verwenden, sieht es so aus:
Die
binding
Hände greifen auf lokale Variablen des ERB-Methodenaufrufs zu, damit sie darauf zugreifen und sie zum Füllen der Vorlage verwenden können. (Der Code zwischen den EOFs ist eine Zeichenfolge, der Teil zwischen <% =%> wird von ERB als Ruby-Code ausgewertet und deklariert seinen eigenen Bereich wie eine Funktion.)Ein Rails-Beispiel zeigt dies noch besser. In einem Artikel-Controller finden Sie ungefähr Folgendes:
Die Datei index.html.erb könnte dann die lokale Variable
@articles
wie folgt verwenden (in diesem Fall werden die Erstellung eines ERB-Objekts und die Bindung vom Rails-Framework verwaltet, sodass Sie sie hier nicht sehen):Durch die Verwendung einer Bindungsvariablen ermöglicht Ruby die Ausführung ein und desselben Vorlagencodes in verschiedenen Kontexten.
Die ERB-Klasse ist nur ein Anwendungsbeispiel. Ruby ermöglicht es im Allgemeinen, den tatsächlichen Ausführungsstatus mit Variablen- und Methodenbindungen mithilfe der Kernel # -Bindung abzurufen. Dies ist sehr nützlich in jedem Kontext, in dem Sie eine Methode in einem anderen Kontext auswerten oder einen Kontext für die spätere Verwendung beibehalten möchten.
quelle
Die Anwendungsfälle für das dynamische Scoping sind IHMO-dieselben wie für globale Variablen. Dynamisches Scoping vermeidet einige Probleme mit globalen Variablen und ermöglicht eine kontrollierte Aktualisierung der Variablen.
Einige Anwendungsfälle, an die ich denken kann:
Natürlich ist dynamisches Scoping nicht "unbedingt erforderlich", sondern entlastet Sie von der Notwendigkeit, Tramp-Daten entlang der Anrufkette weiterzuleiten oder intelligente globale Proxys und Kontextmanager zu implementieren.
Aber auch hier ist dynamisches Scoping praktisch, wenn es um Elemente geht, die häufig als globale Elemente behandelt werden (Protokollierung, Ausgabe, Ressourcenzuweisung / -verwaltung usw.).
quelle
Es gibt so ziemlich keine. Ich hoffe sehr, dass Sie zum Beispiel nie zweimal dieselbe Variable verwenden.
Wie ruft man nun foo und bar von derselben Funktion aus auf?
Dies ähnelt praktisch der einfachen Verwendung einer globalen Variablen und ist aus den gleichen Gründen schlecht.
quelle
x = 42; foo(); x = '42'; bar();
?