Umfang (Informatik)
Im Computerprogrammierung, das Umfang von a Namebindung- eine Vereinigung eines Namens zu einer Entität wie a Variable- Ist der Teil von a Programm Wenn die Namensbindung gültig ist, kann der Name hier verwendet werden, um sich auf die Entität zu beziehen. In anderen Teilen des Programms kann sich der Name auf eine andere Entität beziehen (es kann eine andere Bindung haben) oder auf nichts (es kann ungebunden sein). Umfang hilft, Namenskollisionen zu verhindern. Es ermöglicht den gleichen Namen, sich auf verschiedene Objekte zu beziehen - solange die Namen separate Bereiche haben. Der Umfang einer Namensbindung ist auch als die bezeichnet als die Sichtweite einer Entität, insbesondere in älterer oder mehr technischer Literatur - dies stammt aus der Perspektive der referenzierten Entität, nicht aus dem Referenznamen.
Der Begriff "Bereich" wird auch verwendet, um sich auf den Satz von zu beziehen alle Namensbindungen, die innerhalb eines Teils eines Programms oder an einem bestimmten Punkt in einem Programm gültig sind, das korrekter als als bezeichnet wird Kontext oder Umgebung.[a]
Genau genommen[b] und in der Praxis für die meisten Programmiersprachen bezieht sich "Teil eines Programms" auf einen Teil von Quellcode (Textbereich) und ist bekannt als als lexikalischer Bereich. In einigen Sprachen bezieht sich jedoch "Teil eines Programms" auf einen Teil der Laufzeit (Zeitraum während der Ausführung) und ist als bekannt als als dynamischer Bereich. Beide Begriffe sind etwas irreführend - sie missbrauchen technische Begriffe, wie in der erläutert Definition- Aber die Unterscheidung selbst ist genau und präzise, und dies sind die jeweiligen Standardbegriffe. Der lexikalische Bereich ist der Schwerpunkt dieses Artikels, wobei der dynamische Bereich im Gegensatz zum lexikalischen Bereich verstanden wird.
In den meisten Fällen ist die Namensauflösung basierend auf dem lexikalischen Umfang relativ einfach zu verwenden und zu implementieren, da man in dem Quellcode rückwärts lesen kann, um festzustellen, auf welchen Entität ein Name bezieht, und in der Implementierung kann eine Liste von Namen und Namen führen und verwalten Kontexte beim Kompilieren oder Interpretieren eines Programms. Schwierigkeiten auftreten in Namensmaskierung, Vorwärtsdeklarationen, und Heben, während erheblich subtilere mit entstehen mit Nicht-lokale Variablen, speziell in Schließungen.
Definition
Die strenge Definition des (lexikalischen) "Umfangs" eines Namens (Kennung) ist eindeutig: Der lexikalische Bereich ist "der Teil des Quellcodes, in dem eine Bindung eines Namens mit einer Entität gilt". Dies ist praktisch unverändert gegenüber seiner Definition von 1960 in der Spezifikation von Algol 60. Repräsentative Sprachspezifikationen folgen:
- Algol 60 (1960)[1]
- Die folgenden Arten von Mengen werden unterschieden: einfache Variablen, Arrays, Beschriftungen, Schalter und Verfahren. Der Umfang einer Menge ist der Satz von Aussagen und Ausdrücken, in denen die Erklärung des mit dieser Menge verbundenen Kennung gültig ist.
- C (2007)[2]
- Ein Kennung kann ein Objekt bezeichnen; eine Funktion; ein Tag oder ein Mitglied einer Struktur, Union oder Aufzählung; a Typedef Name; ein Etikettenname; ein Makroname; oder ein Makroparameter. Die gleiche Kennung kann verschiedene Einheiten an verschiedenen Stellen des Programms bezeichnen. [...] Für jede verschiedene Entität, die eine Kennung bezeichnet, ist die Kennung sichtbar (d. H. kann verwendet werden) nur innerhalb eines Programmsbereichs, der als seine genannt wird Umfang.
- gehen (2013)[3]
- Eine Deklaration bindet eine Nicht-Blank-Kennung an eine Konstante, einen Typ, eine Variable, eine Funktion, Beschriftung oder ein Paket. [...] Der Umfang einer deklarierten Kennung ist das Ausmaß des Quelltextes, in dem die Kennung die angegebene Konstante, Typ, Variable, Funktion, Beschriftung oder Paket bezeichnet.
Am häufigsten bezieht Variable-Wenn ein Erklärung hat einen Effekt - kann aber auch für andere Unternehmen gelten, wie z. B. Funktionen, Typen, Klassen, Etiketten, Konstanten und Aufzählungen.
Lexikalischer Bereich gegen dynamischer Umfang
Eine grundlegende Unterscheidung im Umfang ist das "Teil eines Programms". In Sprachen mit lexikalischer Bereich (auch genannt statischer Umfang), Namensauflösung hängt vom Ort im Quellcode und dem ab lexikalischer Kontext (auch genannt statischer Kontext), was definiert wird, wenn die benannte Variable oder Funktion definiert ist. Dagegen in Sprachen mit dynamischer Bereich Die Namensauflösung hängt von der ab Programmstaat Wenn der Name angetroffen wird, wird durch die bestimmt Ausführungskontext (auch genannt Laufzeitkontext, Kontext rufen oder dynamischer Kontext). In der Praxis wird ein Name mit lexikalischem Umfang durchsucht, indem der lokale lexikalische Kontext gesucht wird, wenn dies fehlschlägt, indem der äußere lexikalische Kontext usw. durchsucht wird; Während mit dynamischem Umfang ein Name durchsucht wird, indem der lokale Ausführungskontext gesucht wird, dann, wenn dies fehlschlägt, durch die Suche nach dem äußeren Ausführungskontext usw.[4]
Die meisten modernen Sprachen verwenden einen lexikalischen Umfang für Variablen und Funktionen Vorlagensprachen. [c] Perl 5 bietet sowohl lexikalischen als auch dynamischen Umfang. Sogar in lexikalisch abgelegenen Sprachen, Rahmen für Schließungen Kann für die Uneingeweihten verwirrend sein, da diese vom lexikalischen Kontext abhängen, in dem der Schließ definiert ist, nicht dort, wo er aufgerufen wird.
Die lexikalische Auflösung kann bei bestimmt werden Zeit kompilierenund ist auch bekannt als als Frühe Bindung, während eine dynamische Auflösung im Allgemeinen nur bei bestimmt werden kann Laufzeitund so ist bekannt als späte Bindung.
Verwandte konzepte
Im Objekt orientierte Programmierung, Dynamischer Versand wählt ein Objekt aus Methode Zur Laufzeit hängt jedoch, ob die tatsächliche Namensbindung zur Kompilierungszeit oder zur Laufzeit durchgeführt wird, von der Sprache ab. De facto dynamischer Umfang ist häufig in Makrosprachen, die nicht direkt die Namensauflösung haben, sondern stattdessen expandieren.
Einige Programmierrahmen mögen Angularjs Verwenden Sie den Begriff "Bereich", um etwas ganz anderes zu bedeuten als in diesem Artikel. In diesen Frameworks ist der Umfang nur ein Objekt der Programmiersprache, die sie verwenden (JavaScript im Falle von AngularJs), der auf bestimmte Weise vom Rahmen verwendet wird, um den dynamischen Bereich in einer Sprache zu emulieren, die den lexikalischen Bereich für seine Variablen verwendet. Diese AngularJs Scopes Kann sich in einem bestimmten Teil des Programms im Zusammenhang mit dem Kontext (unter Verwendung der üblichen Bedeutung des Begriffs) befinden oder nicht, folgt den üblichen Regeln des variablen Umfangs der Sprache wie jedes andere Objekt und die Verwendung ihres eigenen Nachlass und Transclusion Regeln. Im Kontext von AngularJs wird manchmal der Begriff "$ scope" (mit einem Dollar -Zeichen) verwendet, um Verwirrung zu vermeiden, aber die Verwendung des Dollar -Anzeichens in variablen Namen wird häufig von den Style -Guides entmutigt.[5]
Verwenden
Umfang ist eine wichtige Komponente von Namensauflösung,[d] was wiederum von grundlegender Bedeutung ist Sprachsemantik. Die Namensauflösung (einschließlich Umfang) variiert zwischen den Programmiersprachen und innerhalb einer Programmiersprache nach Art der Entität. Die Regeln für den Umfang werden genannt Umfangsregeln (oder Scoping -Regeln). Zusammen mit Namespaces, Umfangsregeln sind entscheidend in Modulare ProgrammierungEine Änderung in einem Teil des Programms bricht also keinen nicht verwandten Teil.
Überblick
Bei der Erörterung des Umfangs gibt es drei grundlegende Konzepte: Umfang, Ausmaß, und Kontext. Insbesondere "Scope" und "Kontext" sind häufig verwirrt: Scope ist eine Eigenschaft einer Namensbindung, während der Kontext eine Eigenschaft eines Teils eines Programms ist, der entweder ein Teil des Quellcode ist (Quellcode (lexikalischer Kontext oder statischer Kontext) oder ein Teil von Laufzeit (Ausführungskontext, Laufzeitkontext, Kontext rufen oder dynamischer Kontext). Ausführungskontext besteht aus dem lexikalischen Kontext (am aktuellen Ausführungspunkt) plus zusätzlichem Laufzeitzustand wie die Rufen Sie Stack an.[e] Streng genommen wird während der Ausführung ein Programm in verschiedene Bereiche der Namensbindungen ein und verlässt. "Wenn die Programmausführung in den Umfang eingeht oder verlässt.[f] In der Praxis ist die Verwendung jedoch viel lockerer.
Geltungsbereich ist ein Konzept der Quellcode-Ebene und eine Eigenschaft von Namensbindungen, insbesondere variable oder Funktionsnamenbindungen-Names im Quellcode sind Verweise an Entitäten im Programm - und ist Teil des Verhaltens eines Compilers oder Interpreters einer Sprache. Daher sind Fragen des Umfangs ähnlich wie Zeiger, die eine Art von Referenz sind, die in Programmen allgemeiner verwendet werden. Verwenden des Wertes einer Variablen, wenn sich der Name im Kontext befindet, aber die Variable nicht initialisiert ist Wild Zeigerwie es undefiniert ist. Da Variablen jedoch erst zerstört werden, wenn sie aus dem Kontext gehen, das Analogon von a baumelnder Zeiger ist nicht vorhanden.
Für Entitäten wie Variablen ist der Umfang eine Teilmenge von Lebensdauer (auch bekannt als Ausmaß) - Ein Name kann sich nur auf eine Variable beziehen, die existiert (möglicherweise mit undefiniertem Wert), aber Variablen, die existieren, sind nicht unbedingt sichtbar: Eine Variable kann vorhanden, aber unzugänglich sein (der Wert wird in einem bestimmten Kontext nicht bezeichnet), oder zugänglich, aber nicht über den angegebenen Namen, in diesem Fall ist es nicht im Kontext (das Programm ist "aus dem Rahmen des Namens"). In anderen Fällen ist "Lebensdauer" irrelevant - ein Etikett (benannte Position im Quellcode) hat eine Lebensdauer mit dem Programm (für staatlich kompilierte Sprachen), kann jedoch in einem bestimmten Punkt im Programm und ebenfalls in Kontext sein und ebenfalls für Statische Variablen-a Statische globale Variable ist im Kontext für das gesamte Programm, während a Statische lokale Variable ist nur im Kontext innerhalb einer Funktion oder eines anderen lokalen Kontextes, aber beide haben Lebensdauer des gesamten Programms.
Die Bestimmung, auf welcher Entität ein Name bezieht Namensauflösung oder Namebindung (speziell in Objekt orientierte Programmierung) und variieren zwischen Sprachen. Bei einem Namen prüft die Sprache (ordnungsgemäß, der Compiler oder Interpreter) alle Entitäten, die im Kontext für Übereinstimmungen stehen. Im Falle von Unklarheiten (zwei Entitäten mit demselben Namen, wie z. B. eine globale und lokale Variable mit demselben Namen) werden die Namen der Namensauflösung verwendet, um sie zu unterscheiden. Am häufigsten stützt sich die Namensauflösung auf einer "inneren Kontext-Kontext-Regel" wie der Python-Legb-Regel (lokal, eingeschlossen, global, integriert). In einigen Fällen kann die Namensauflösung explizit angegeben werden, wie beispielsweise von der global
und nicht lokal
Schlüsselwörter in Python; In anderen Fällen können die Standardregeln nicht außer Kraft gesetzt werden.
Wenn zwei identische Namen gleichzeitig im Kontext stehen und sich auf verschiedene Entitäten beziehen, sagt man das Namensmaskierung tritt auf, wenn der Name der höheren Priorität (normalerweise innerstes) den Namen der niedrigeren Priorität "maskiert". Auf der Ebene der Variablen ist dies als bekannt als variabler Schatten. Aufgrund des Potenzials für Logikfehler Von Maskierung verbieten oder entmutigen einige Sprachen die Maskierung, die einen Fehler oder eine Warnung zur Kompilierung Zeit oder Laufzeit erhöhen.
Verschiedene Programmiersprachen Haben Sie verschiedene Bereiche für verschiedene Arten von Erklärungen und Namen. Solche Umfangsregeln haben einen großen Einfluss auf Sprachsemantik und folglich über das Verhalten und die Korrektheit von Programmen. In Sprachen wie C ++Der Zugriff auf eine ungebundene Variable hat keine genau definierte Semantik und kann dazu führen undefiniertes Verhalten, ähnlich wie bei A. baumelnder Zeiger; und Erklärungen oder Namen, die außerhalb ihres Geltungsbereichs verwendet werden Syntaxfehler.
Scopes sind häufig an andere Sprachkonstrukte gebunden und implizit bestimmt, aber viele Sprachen bieten auch Konstrukte speziell für die Kontrolle des Umfangs.
Umfangsniveaus
Der Umfang kann von nur einem einzigen Ausdruck bis zu dem gesamten Programm variieren, wobei viele mögliche Abstufungen dazwischen sind. Die einfachste Umfangsregel ist der globale Umfang - alle Unternehmen sind während des gesamten Programms sichtbar. Die grundlegendste modulare Umfangsregel ist zwei Ebenen, ein globaler Bereich überall im Programm und lokaler Umfang innerhalb einer Funktion. Die komplexere modulare Programmierung ermöglicht ein separates Modulbereich, bei dem die Namen im Modul (privat für das Modul) sichtbar sind, aber außerhalb nicht sichtbar sind. Innerhalb einer Funktion erlauben einige Sprachen, wie C, Blockbereich auf eine Teilmenge einer Funktion einschränken. Andere, insbesondere funktionale Sprachen, erlauben den Ausdrucksumfang, um den Umfang auf einen einzelnen Ausdruck zu beschränken. Andere Bereiche umfassen den Dateiumfang (insbesondere in C), das sich ähnlich wie der Modulbereich verhält, und blockieren den Umfang außerhalb von Funktionen (insbesondere in Perl).
Ein subtiles Problem ist genau dann, wenn ein Zielfernrohr beginnt und endet. In einigen Sprachen wie C beginnt ein Namensumfang mit der Namenserklärung, und somit können unterschiedliche Namen, die in einem bestimmten Block deklariert sind, unterschiedliche Bereiche haben. Dies erfordert die Erklärung von Funktionen vor der Verwendung, wenn auch nicht unbedingt definiert und erfordert und erfordert Vorwärtsdeklaration In einigen Fällen, insbesondere für die gegenseitige Rekursion. In anderen Sprachen wie Python beginnt ein Name eines Namens zu Beginn des relevanten Blocks, in dem der Name deklariert wird (z. Gleicher Umfang. In JavaScript der Umfang eines Namens mit dem Namen mit dem Namen Lassen
oder Const
beginnt mit der Namenserklärung und dem Umfang eines Namens, der mit erklärt wird var
beginnt zu Beginn der Funktion, in der der Name deklariert wird, was als bekannt ist Variable Hebezeuge. Das Verhalten von Namen im Kontext, der undefinierter Wert hat, unterscheidet var
sind während der gesamten Funktion nutzbar, weil sie implizit an den Wert gebunden sind nicht definiert
.
Ausdrucksumfang
Der Umfang einer Namensbindung ist ein Ausdruck, was bekannt ist als Ausdrucksumfang. Ausdrucksumfang ist insbesondere in vielen Sprachen erhältlich funktional Sprachen, die eine Funktion namens anbieten Let-Expressionen Erlauben, dass der Umfang einer Erklärung ein einziger Ausdruck ist. Dies ist zweckmäßig, wenn beispielsweise für eine Berechnung ein Zwischenwert erforderlich ist. Zum Beispiel in Standard ml, wenn f()
kehrt zurück 12
, dann Sei Val x = f () in x * x End
ist ein Ausdruck, der bewertet 144
mit einer vorübergehenden Variablen benannt x
Anrufen zu vermeiden f()
zweimal. Einige Sprachen mit Blockbereich nähern sich diese Funktionalität, indem sie Syntax für einen Block anbieten, um in einen Ausdruck eingebettet zu werden. Zum Beispiel könnte der oben erwähnte Standard -ML -Ausdruck in geschrieben werden Perl wie do { my $x = f(); $x * $x }
, oder in Gnu c wie ({ int x = f(); x * x; })
.
In Python haben Hilfsvariablen in Generatorausdrücken und Listenverständnissen (in Python 3) Expressionsbereich.
In C variable Namen in a Funktionsprototyp den Ausdrucksumfang haben, der in diesem Zusammenhang bekannt ist als Funktionsprotokollbereich. Da die Variablennamen im Prototyp nicht genannt werden (sie können in der tatsächlichen Definition unterschiedlich sein) - sind sie nur Dummies -, diese werden häufig weggelassen, obwohl sie beispielsweise zur Generierung von Dokumentationen verwendet werden können.
Blockbereich
Der Umfang einer Namensbindung ist a Block, was bekannt ist als Blockbereich. Blockbereich ist in vielen, aber nicht allen blockstrukturierten Programmiersprachen erhältlich. Dies begann mit Algol 60, wo "[e] sehr deklaration ... nur für diesen Block gültig ist",,[6] und heute ist besonders mit Sprachen in der verbunden Pascal und C Familien und Traditionen. Meistens ist dieser Block in einer Funktion enthalten, wodurch das Zielfernrohr auf einen Teil einer Funktion eingeschränkt wird, in einigen Fällen, wie z. B. Perl, ist der Block möglicherweise nicht innerhalb einer Funktion.
ohne Vorzeichen int Quadratsumme(Const ohne Vorzeichen int N) { ohne Vorzeichen int ret = 0; zum (ohne Vorzeichen int n = 1; n <= N; n++) { Const ohne Vorzeichen int N_quared = n * n; ret += N_quared; } Rückkehr ret; }
Ein repräsentatives Beispiel für die Verwendung des Blockbereichs ist der hier gezeigte C -Code, in dem zwei Variablen in die Schleife geschoben werden: die Schleifenvariable n, der einmal initialisiert und bei jeder Iteration der Schleife und der Hilfsvariable erhöht wird N_quared, was bei jeder Iteration initialisiert wird. Ziel ist es, das Funktionsbereich zu vermeiden, Variablen hinzuzufügen, die nur für einen bestimmten Block relevant sind. Dies verhindert beispielsweise Fehler, bei denen die generische Schleifenvariable i wurde versehentlich bereits auf einen anderen Wert eingestellt. In diesem Beispiel der Ausdruck n * n
würde im Allgemeinen nicht einer Hilfsvariablen zugeordnet, und der Körper der Schleife würde einfach geschrieben werden ret += n * n
In komplizierteren Beispielen sind jedoch Hilfsvariablen nützlich.
Blöcke werden hauptsächlich für den Kontrollfluss verwendet, z. B. mit if, während und für Schleifen, und in diesen Fällen bedeutet Blockbereich, dass der Variablenumfang von der Struktur des Ausführungsflusss einer Funktion abhängt. Sprachen mit Blockbereich ermöglichen jedoch normalerweise auch die Verwendung von "nackten" Blöcken, deren alleiniger Zweck darin besteht, eine feinkörnige Kontrolle des variablen Bereichs zu ermöglichen. Beispielsweise kann eine Hilfsvariable in einem Block definiert und dann verwendet werden (z. B. einer Variablen mit Funktionsbereich) und verworfen werden, wenn der Block endet, oder eine while -Schleife möglicherweise in einem Block eingeschlossen sein, der Variablen initialisiert, die in der Schleife verwendet werden Das sollte nur einmal initialisiert werden.
Eine Subtilität mehrerer Programmiersprachen, wie z. Algol 68 und c (in diesem Beispiel demonstriert und seitdem standardisiert C99), ist, dass Block-Scope-Variablen nicht nur im Körper des Blocks, sondern auch innerhalb der Kontrollanweisung deklariert werden können. Dies ist analog zu Funktionsparametern, die in der Funktionserklärung (vor dem Start des Funktionsblocks) und im Bereich für den gesamten Funktionskörper deklariert sind. Dies wird in erster Linie in verwendet für Schleifen, die eine Initialisierungsanweisung haben, die von der Schleifenbedingung getrennt ist, im Gegensatz zu Schleifen und eine gemeinsame Idiom ist.
Blockbereich kann zum Schatten verwendet werden. In diesem Beispiel hätte auch im Block die Hilfsvariable genannt werden können nden Parameternamen beschattet, aber dies wird aufgrund des Fehlerpotentials als schlechter Stil angesehen. Darüber hinaus lassen einige Nachkommen von C, wie Java und C#, trotz Unterstützung des Blockbereichs (insofern eine lokale Variable vor dem Ende einer Funktion aus dem Kontext aus dem Kontext gestellt werden kann) keine lokale Variable, um eine andere zu verbergen . In solchen Sprachen die versuchte Erklärung der zweiten n würde zu einem Syntaxfehler und einem der der n Variablen müssten umbenannt werden.
Wenn ein Block verwendet wird, um den Wert einer Variablen festzulegen, muss der Blockbereich außerhalb des Blocks deklariert werden. Dies kompliziert die Verwendung bedingter Aussagen mit einzelne Zuordnung. In Python, bei dem kein Blockumfang verwendet wird, kann beispielsweise eine Variable als solche initialisieren:
wenn c: a = "Foo" anders: a = "" "
wo a
ist nach dem zugänglich wenn
Aussage.
In Perl, der Blockbereich hat, muss stattdessen die Variable vor dem Block deklariert werden:
mein $ a; wenn (c) { $ a = "Foo"; } anders { $ a = '' '; }
Oft wird dies stattdessen mit mehreren Zuordnungen umgeschrieben, wobei die Variable in einen Standardwert initialisiert wird. In Python (wo es nicht notwendig ist) wäre dies:
a = "" " wenn c: a = "Foo"
Während in Perl wäre dies:
mein $ a = '' '; wenn (c) { $ a = "Foo"; }
Bei einer einzelnen variablen Zuordnung besteht eine Alternative darin, die zu verwenden ternärer Operator Um einen Block zu vermeiden, ist dies jedoch im Allgemeinen für mehrere variable Zuordnungen nicht möglich und ist für komplexe Logik schwer zu lesen.
Dies ist ein wichtigeres Problem in C, insbesondere für die Stringzuweisung, da die String -Initialisierung automatisch Speicher zuweisen kann, während die String -Zuordnung zu einer bereits initialisierten Variablen die Zuordnungsspeicher, eine String -Kopie und die Überprüfung der erfolgreichen Überprüfung erfordert.
{ mein $ counter = 0; Sub Increment_counter { Rückkehr ++$ counter; } }
In einigen Sprachen kann das Konzept des Blockbereichs in unterschiedlichem Ausmaß außerhalb einer Funktion angewendet werden. Zum Beispiel im Perl -Snippet rechts, $ counter
ist ein variabler Name mit Blockbereich (aufgrund der Verwendung des mein
Schlüsselwort), während Increment_counter
ist ein Funktionsname mit globalem Umfang. Jeder Anruf an Increment_counter
wird den Wert von erhöhen $ counter
um eins und geben Sie den neuen Wert zurück. Code außerhalb dieses Blocks kann anrufen Increment_counter
,, kann aber nicht sonst den Wert von erhalten oder ändern $ counter
. Mit diesem Idiom kann ein Verschluss in Perl definieren.
Funktionsumfang
Wenn der Umfang der in einer Funktion deklarierter Variablen nicht über diese Funktion hinausgeht, wird dies als bezeichnet Funktionsumfang.[7] Der Funktionsumfang ist in den meisten Programmiersprachen verfügbar, die eine Möglichkeit bieten, a zu erstellen lokale Variable in einer Funktion oder Subroutine: Eine Variable, deren Umfang endet (der aus dem Kontext geht), wenn die Funktion zurückgibt. In den meisten Fällen ist die Lebensdauer der Variablen die Dauer des Funktionsaufrufs - es ist ein Automatische Variable, erstellt, wenn die Funktion startet (oder die Variable deklariert wird), zerstört, wenn die Funktion zurückkehrt - während der Umfang der Variablen innerhalb der Funktion liegt, obwohl die Bedeutung von "innerhalb" davon abhängt, ob der Umfang lexikalisch oder dynamisch ist. Einige Sprachen, wie C, sorgen jedoch auch für Statische lokale Variablen, wo die Lebensdauer der Variablen die gesamte Lebensdauer des Programms ist, aber die Variable befindet sich nur im Kontext im Inneren der Funktion. Bei statischen lokalen Variablen wird die Variable erstellt, wenn das Programm initialisiert und nur zerstört wird, wenn das Programm endet, wie bei a Statische globale Variable, aber nur im Kontext innerhalb einer Funktion, wie eine automatische lokale Variable.
Wichtig ist, dass im lexikalischen Bereich eine Variable mit Funktionsumfang nur innerhalb des lexikalischer Kontext der Funktion: Es geht aus dem Zusammenhang, wenn eine andere Funktion innerhalb der Funktion aufgerufen wird, und kommt in den Kontext zurück, wenn die Funktion zurückkehrt. Körper der Funktion, in der sie deklariert werden. Im dynamischen Umfang erstreckt sich das Zielfernrohr dagegen auf die Ausführungskontext der Funktion: Lokale Variablen Bleib im Kontext Wenn eine andere Funktion aufgerufen wird und nur dann aus dem Kontext geht, wenn die Definitionsfunktion endet, und daher lokale Variablen im Kontext der Funktion stehen, in der sie definiert sind und alle als Funktionen bezeichnet. In Sprachen mit lexikalischer Anwendung und verschachtelte Funktionen, lokale Variablen stehen im Zusammenhang mit verschachtelten Funktionen, da diese innerhalb desselben lexikalischen Kontexts, jedoch nicht für andere Funktionen, die nicht lexikalisch verschachtelt sind. Eine lokale Variable einer umschließenden Funktion ist als als bekannt Nicht-lokale Variable für die verschachtelte Funktion. Der Funktionsumfang ist auch anwendbar für Anonyme Funktionen.
def Quadrat(n): Rückkehr n * n def Quadratsumme(n): gesamt = 0 i = 0 während i <= n: gesamt += Quadrat(i) i += 1 Rückkehr gesamt
Zum Beispiel werden im Ausschnitt des Python -Code rechts zwei Funktionen definiert: Quadrat
und Quadratsumme
. Quadrat
berechnet das Quadrat einer Zahl; Quadratsumme
Berechnet die Summe aller Quadrate bis zu einer Zahl. (Zum Beispiel, Quadrat (4)
ist 42=16
, und sum_of_squares (4)
ist 02+12+22+32+42=30
.))
Jede dieser Funktionen hat eine Variable benannt n Das stellt das Argument für die Funktion dar. Diese zwei n Variablen sind völlig getrennt und nicht miteinander verbunden, obwohl sie denselben Namen haben, da sie lexikalisch lokale Variablen mit Funktionsbereich sind: Jedes einzelne Bereich ist seine eigene, lexikalisch getrennte Funktion und überlappen sich daher nicht. Deswegen, Quadratsumme
kann anrufen Quadrat
ohne seine eigenen n verändert sein. Ähnlich, Quadratsumme
hat Variablen benannt gesamt und i; Diese Variablen stören aufgrund ihres begrenzten Umfangs keine benannten Variablen gesamt oder i Das könnte zu jeder anderen Funktion gehören. Mit anderen Worten, es besteht kein Risiko von a Nennen Sie Kollision Zwischen diesen Namen und allen nicht verwandten Namen, auch wenn sie identisch sind.
Es tritt keine Namensmaskierung auf: Nur eine Variable benannt n ist zu einem bestimmten Zeitpunkt im Kontext, da sich die Bereiche nicht überlappen. Im Gegensatz dazu waren ein ähnliches Fragment, das in einer Sprache mit dynamischem Umfang geschrieben wurde, die n In der Aufruffunktion würde in der aufgerufenen Funktion im Kontext bleiben - die Bereiche würden sich überschneiden - und würden von den neuen maskiert ("beschattet") n in der aufgerufenen Funktion.
Der Funktionsumfang ist wesentlich komplizierter, wenn Funktionen erstklassige Objekte sind und lokal zu einer Funktion erstellt und dann zurückgegeben werden können. In diesem Fall gibt es Variablen in der verschachtelten Funktion, die nicht lokal für sie sind (ungebundene Variablen in der Funktionsdefinition, die auf Variablen in einem umschließenden Kontext auflösen) a erstellen a Schließung, wie nicht nur die Funktion selbst, sondern auch ihr Kontext (von Variablen) zurückgegeben und dann möglicherweise in einem anderen Kontext aufgerufen werden. Dies erfordert erhebliche Unterstützung durch den Compiler und kann die Programmanalyse komplizieren.
Dateiumfang
Der Umfang einer Namensbindung ist eine Datei, die als bekannt ist Dateiumfang. Dateiumfang ist weitgehend speziell zu C (und C ++), wobei Variablen und Funktionen auf der oberen Ebene einer Datei (nicht innerhalb einer Funktion) für die gesamte Datei oder eher für C von der Deklaration bis zum Ende von deklariert sind die Quelldatei oder genauer Übersetzungseinheit (interne Verknüpfung). Dies kann als Form des Modulbereichs angesehen werden, bei dem Module mit Dateien identifiziert werden und in moderneren Sprachen durch einen expliziten Modulbereich ersetzt werden. Aufgrund des Vorhandenseins von Einschlusserklärungen, die dem internen Kontext Variablen und Funktionen hinzufügen und selbst weitere Einfügungsanweisungen aufrufen können, kann es schwierig sein, zu bestimmen, was sich im Körper einer Datei im Kontext befindet.
Im C -Code -Snippet oben der Funktionsname Quadratsumme
hat Dateiumfang.
Modulbereich
Der Umfang einer Namensbindung ist ein Modul, das als bekannt ist als Modulbereich. Modulbereich ist in erhältlich Modulare Programmiersprachen Wo Module (die verschiedene Dateien umfassen können) die grundlegende Einheit eines komplexen Programms sind, da sie Informationen verstecken und eine begrenzte Schnittstelle freigeben können. Der Modulbereich wurde in der Pionierarbeit geleistet Modula Familie der Sprachen und Python (das von Modula beeinflusst wurde) ist ein repräsentatives zeitgemäßes Beispiel.
In einigen Objekt orientierte Programmierung Sprachen, denen Module wie C ++ direkt unterstützt werden, wird stattdessen eine ähnliche Struktur durch die Klassenhierarchie bereitgestellt, wobei Klassen die grundlegende Einheit des Programms sind und eine Klasse private Methoden haben kann. Dies wird im Kontext von richtig verstanden Dynamischer Versand Anstatt Namensauflösung und Umfang, obwohl sie oft analoge Rollen spielen. In einigen Fällen sind beide Einrichtungen verfügbar, z.
Globaler Umfang
Der Umfang einer Namensbindung ist ein ganzes Programm, das als bekannt ist als globaler Umfang. Variablennamen mit globalem Umfang - genannt globale Variablen- Es wird häufig als schlechte Praxis angesehen, zumindest in einigen Sprachen, aufgrund der Möglichkeit von Namenskollisionen und unbeabsichtigten Maskierungen zusammen mit schlechter Modularität, und der Funktionsumfang oder der Blockbereich werden als bevorzugt angesehen. Der globale Umfang wird jedoch normalerweise (abhängig von der Sprache) für verschiedene andere Arten von Namen verwendet, z. B. Namen von Funktionen, Namen von Klassen und Namen anderer Datentypen. In diesen Fällen Mechanismen wie z. Namespaces werden verwendet, um Kollisionen zu vermeiden.
Lexikalischer Bereich gegen dynamischer Umfang
Die Verwendung lokaler Variablen - von Variablennamen mit begrenztem Umfang, die nur innerhalb einer bestimmten Funktion existieren - hilft, das Risiko einer Namenskollision zwischen zwei identisch benannten Variablen zu vermeiden. Es gibt jedoch zwei sehr unterschiedliche Ansätze zur Beantwortung dieser Frage: Was bedeutet es, "innerhalb" einer Funktion zu sein?
Im lexikalischer Bereich (oder lexikaler Scoping; auch genannt statischer Umfang oder statischer Scoping) Wenn der Bereich eines Variablennamens eine bestimmte Funktion ist, ist sein Umfang der Programmtext der Funktionsdefinition: In diesem Text existiert der Variablenname und ist an den Wert der Variablen gebunden, aber außerhalb dieses Text nicht existieren. Dagegen in dynamischer Bereich (oder Dynamischer Scoping) Wenn der Umfang eines Variablennamens eine bestimmte Funktion ist, ist sein Umfang die Zeitperiode, in der die Funktion ausgeführt wird: Während die Funktion ausgeführt wird, existiert der variable Name und ist an ihren Wert gebunden, aber nachdem die Funktion zurückgibt Der variable Name existiert nicht. Dies bedeutet, dass wenn Funktion f
ruft eine separat definierte Funktion auf g
, dann unter lexikalischer Umfang, Funktion g
tut nicht Zugang haben zu f
lokale Variablen (unter der Annahme des Textes von g
ist nicht im Text von f
), während sie unter dynamischem Bereich, Funktion g
tut Zugang haben zu f
lokale Variablen (seitdem g
wird während der Anrufung von aufgerufen f
).
$ # Bash -Sprache $ x=1 $ Funktion g() { Echo $ x ; x=2 ; } $ Funktion f() { lokal x=3 ; g ; } $ f # Drucken Sie diesen oder 3? 3 $ Echo $ x # Drucken Sie diesen oder 2? 1
Betrachten Sie zum Beispiel das Programm rechts. Die erste Zeile, x=1
erstellt eine globale Variable x
und initialisiert es zu 1
. Die zweite Zeile, function g() { echo $x ; x=2 ; }
definiert eine Funktion g
das druckt den aktuellen Wert von ("echoes") aus x
und dann setzt x
zu 2
(Überschreiben des vorherigen Wertes). Die dritte Zeile, function f() { local x=3 ; g ; }
definiert eine Funktion f
Das schafft eine lokale Variable x
(verstecken die identisch benannte globale Variable) und initialisiert sie an 3
, und dann ruft g
. Die vierte Zeile, f
, Anrufe f
. Die fünfte Zeile, echo $x
druckt den aktuellen Wert von aus x
.
Was genau druckt dieses Programm? Es hängt von den Umfangsregeln ab. Wenn die Sprache dieses Programms einer der lexikalische Umfang verwendet wird, dann g
druckt und verändert die globale Variable x
(Weil g
ist draußen definiert f
), so druckt das Programm 1
und dann 2
. Im Gegensatz dazu, wenn diese Sprache dynamischen Umfang verwendet, dann g
druckt und modifiziert f
lokale Variable x
(Weil g
wird von innen gerufen f
), so druckt das Programm 3
und dann 1
. (Wie es passiert, ist die Sprache des Programms Verprügeln, der dynamischen Bereich verwendet; Das Programm druckt also 3
und dann 1
. Wenn der gleiche Code mit ausgeführt wurde mit KSH93 Was den lexikalischen Umfang verwendet, wären die Ergebnisse unterschiedlich.)
Lexikalischer Bereich
Mit lexikalischer BereichEin Name bezieht sich immer auf seinen lexikalischen Kontext. Dies ist eine Eigenschaft des Programmtextes und wird unabhängig von der Laufzeit gemacht Rufen Sie Stack an durch die Sprachimplementierung. Da diese Übereinstimmung nur eine Analyse des statischen Programmtextes erfordert, wird auch dieser Typ von Umfang genannt statischer Umfang. Der lexikalische Bereich ist insgesamt Standard Algol-basierte Sprachen wie z. Pascal, Modula-2 und Ada sowie in modernen funktionalen Sprachen wie z. Ml und Haskell. Es wird auch in der verwendet C Sprache und seine syntaktischen und semantischen Verwandten, wenn auch mit unterschiedlichen Arten von Einschränkungen. Der statische Umfang ermöglicht es dem Programmierer, über Objektreferenzen wie Parameter, Variablen, Konstanten, Typen, Funktionen usw. als einfache Namensubstitutionen zu begründen. Dies erleichtert es viel einfacher, modulare Code und Vernunft dafür zu erstellen, da die lokale Namensnamenstruktur isoliert verstanden werden kann. Im Gegensatz dazu zwingt der dynamische Bereich den Programmierer, alle möglichen Ausführungskontexte zu antizipieren, in denen der Code des Moduls aufgerufen werden kann.
Programm A; var I:ganze Zahl; K:verkohlen; Verfahren B; var K:real; L:ganze Zahl; Verfahren C; var M:real; Start (*Zielfernrohr A+B+C*) Ende; (*Zielfernrohr A+B*) Ende; (*Scope a*) Ende.
Zum Beispiel ist Pascal lexikalisch abgeschottet. Betrachten Sie das Pascal -Programmfragment rechts. Die Variable I
ist an allen Punkten sichtbar, weil es niemals von einer anderen gleichnamigen Variablen versteckt wird. Das verkohlen
Variable K
ist nur im Hauptprogramm sichtbar, weil es von der versteckt wird real
Variable K
im Verfahren sichtbar B
und C
nur. Variable L
ist auch nur im Verfahren sichtbar B
und C
Aber es verbergt keine andere Variable. Variable M
ist nur im Verfahren sichtbar C
und daher auch nicht von der Prozedur zugänglich B
oder das Hauptprogramm. Auch Verfahren C
ist nur im Verfahren sichtbar B
und kann daher nicht aus dem Hauptprogramm aufgerufen werden.
Es hätte ein weiteres Verfahren geben können C
im Programm außerhalb des Verfahrens deklariert B
. Der Ort im Programm wo "C
"wird erwähnt, welche der beiden genannten Verfahren genannt wird C
Es repräsentiert also genau analog mit dem Umfang der Variablen.
Richtige Implementierung des lexikalischen Umfangs in Sprachen mit erste Klasse verschachtelte Funktionen ist nicht trivial, da jeder Funktionswert einen Datensatz der Werte der Variablen mit sich tragen muss, von denen er abhängt (das Paar der Funktion und dieser Kontext werden als a Schließung). Abhängig von der Implementierung und Rechnerarchitektur, variabel Sieh nach oben kann etwas ineffizient werden, wenn es sehr lexikalisch tief ist verschachtelt Funktionen werden verwendet, obwohl es bekannte Techniken gibt, um dies zu mildern.[8][9] Für verschachtelte Funktionen, die sich nur auf ihre eigenen Argumente und (sofort) lokale Variablen beziehen, können alle relativen Standorte bei bekannt sein Zeit kompilieren. Bei der Verwendung dieser Art von verschachtelter Funktion entstehen daher überhaupt kein Overhead. Gleiches gilt für bestimmte Teile eines Programms, in dem verschachtelte Funktionen nicht verwendet werden, und natürlich für Programme in einer Sprache, in der verschachtelte Funktionen nicht verfügbar sind (z. B. in der C -Sprache).
Geschichte
Der lexikalische Bereich wurde erstmals in den frühen 1960er Jahren für die imperative Sprache verwendet Algol 60 und wurde seitdem in den meisten anderen imperativen Sprachen aufgegriffen.[4]
Sprachen mögen Pascal und C Hab schon immer einen lexikalischen Umfang, da beide von den Ideen beeinflusst werden Algol 60 und Algol 68 (Obwohl C nicht lexikalisch aufgenommen hat verschachtelte Funktionen).
Perl ist eine Sprache mit dynamischem Umfang, der danach einen statischen Bereich hinzugefügt hat.
Das Original Lispeln Interpreter (1960) verwendete dynamische Umfang. Tiefe Bindung, der sich einem statischen (lexikalischen) Umfang annähert, wurde um 1962 in LiSP 1.5 (über die Funarg Gerät entwickelt von Steve Russell, Arbeiten unter John McCarthy).
Alles früh LISPs verwendet dynamischem Umfang, wenn sie auf Dolmetschern basieren. 1982 Publish, Guy L. Steele Jr. und The Common Lisp Group Ein Überblick über Common Lisp,[10] Eine kurze Überprüfung der Geschichte und der unterschiedlichen Implementierungen von Lisp bis zu diesem Moment und eine Überprüfung der Merkmale, die a Common Lisp Implementierung sollte haben. Auf Seite 102 lesen wir:
Die meisten LISP -Implementierungen sind intern inkonsistent, da der Dolmetscher und der Compiler standardmäßig die korrigierten Programme unterschiedliche Semantik zuweisen können. Dies beruht hauptsächlich aus der Tatsache, dass der Dolmetscher alle Variablen dynamisch abgefeuert wird, während der Compiler alle Variablen annimmt, sofern nicht gezwungen zu sein, etwas anderes anzunehmen. Dies wurde aus Gründen der Bequemlichkeit und Effizienz geschehen, kann aber zu sehr subtilen Fehler führen. Die Definition von Common Lisp vermeidet solche Anomalien, indem der Dolmetscher und der Compiler explizit erforderlich sind, um korrekte Programme identische Semantik aufzuzwingen.
Implementierungen von Common Lisp mussten daher haben lexikalischer Bereich. Wieder von Ein Überblick über Common Lisp:
Darüber hinaus bietet Common Lisp die folgenden Einrichtungen (von denen die meisten von Maclisp-, Interisp- oder Lisp -Maschinen -LISP entlehnt werden): (...) vollständig lexikalisch abgeschottete Variablen. Das sogenannte "Funarg-Problem"[11][12] ist vollständig in den Abwärts- und Aufwärtsfällen gelöst.
Im selben Jahr, in dem Ein Überblick über Common Lisp wurde veröffentlicht (1982), erste Entwürfe (auch von Guy L. Steele Jr.) eines zusammengestellten, lexikalisch abgestellten Lisp, genannt Planen war veröffentlicht und Compiler -Implementierungen wurden versucht. Zu dieser Zeit wurde allgemein befürchtet, dass der lexikalische Umfang in LISP als ineffizient für die Umsetzung befürchtet wurde. Im Eine Geschichte von t,[13] Olin Shivers schreibt:
Alle Zu dieser Zeit wurden schwerwiegende Lis in der Produktionsgebrauch dynamisch abgebildet. Niemand, der das Kaninchen nicht sorgfältig gelesen hatte[14] These (geschrieben von Guy Lewis Steele Jr. im Jahr 1978) glaubte, dass der lexikalische Bereich fliegen würde; Sogar die wenigen Leute, die hatte Lesen Sie es dauerte ein bisschen Glaubenssprung, dass dies in ernsthaften Produktionsgebrauch funktionieren würde.
Der Begriff "lexikalischer Bereich" stammt aus dem mindestens 1967,[15] während der Begriff "lexikalischer Scoping" mindestens 1970 stammt, wo er in verwendet wurde Projekt MAC Um die Umfangsregeln des Lisp -Dialekts zu beschreiben Mdl (Dann als "durcheinander" bekannt).[16]
Dynamischer Bereich
Mit dynamischer BereichEin Name bezieht sich auf den Ausführungskontext. Es ist ungewöhnlich in modernen Sprachen.[4] In technischer Hinsicht bedeutet dies, dass jeder Name eine globale hat Stapel von Bindungen. Einführung einer lokalen Variablen mit Namen x
drückt eine Bindung auf die globale x
Stack (der möglicherweise leer war), der abgetaucht ist, wenn der Steuerfluss verlässt den Bereich. Bewertung x
In jedem Kontext liefert immer die Top -Bindung. Beachten Sie, dass dies nicht zur Kompilierungszeit erfolgen kann, da der Bindungsstapel nur bei vorhanden ist LaufzeitDeshalb wird diese Art von Umfang genannt dynamisch Umfang.
Im Allgemeinen sicher Blöcke werden definiert, um Bindungen zu erstellen, deren Lebensdauer die Ausführungszeit des Blocks ist; Dies fügt dem dynamischen Umfangsprozess einige Funktionen des statischen Bereichs hinzu. Da jedoch ein Codeabschnitt aus vielen verschiedenen Orten und Situationen aufgerufen werden kann, kann es schwierig sein, zu Beginn zu bestimmen, welche Bindungen angewendet werden, wenn eine Variable verwendet wird (oder ob überhaupt). Dies kann vorteilhaft sein; Anwendung der Prinzip des geringsten Wissens schlägt vor, dass der Code je nach dem vermeiden Gründe dafür für (oder Umstände des) Wertes einer Variablen, aber einfach den Wert gemäß der Definition der Variablen verwenden. Diese enge Interpretation gemeinsamer Daten kann ein sehr flexibles System zur Anpassung des Verhaltens einer Funktion an den aktuellen Zustand (oder die Richtlinie) des Systems liefern. Dieser Nutzen beruht jedoch auf sorgfältige Dokumentation aller auf diese Weise verwendeten Variablen sowie auf sorgfältige Vermeidung von Annahmen über das Verhalten einer Variablen und bietet keinen Mechanismus, um Interferenzen zwischen verschiedenen Teilen eines Programms zu erkennen. Einige Sprachen wie Perl und Common LispErmöglichen Sie dem Programmierer, einen statischen oder dynamischen Bereich zu wählen, wenn Sie eine Variable definieren oder neu definieren. Beispiele für Sprachen, die den dynamischen Umfang verwenden Logo, EMACS Lisp, Latex und die Shellsprachen verprügeln, Bindestrich, und Power Shell.
Der dynamische Umfang ist ziemlich einfach zu implementieren. Um einen Namen des Namens zu finden, könnte das Programm den Laufzeitstapel durchqueren und jeden Aktivierungsdatensatz (Stapelrahmen der einzelnen Funktions) auf einen Wert für den Namen überprüfen. In der Praxis wird dies durch die Verwendung eines effizienter gemacht Assoziationsliste, das ist ein Stapel Namen/Wertpaare. Paare werden auf diesen Stapel gedrückt, wenn Deklarationen abgegeben werden, und werden immer dann aufgetaucht, wenn Variablen den Kontext herausgehen.[17] Flache Bindung ist eine alternative Strategie, die erheblich schneller ist und a nutzt a Zentrale Referenztabelle, der jeden Namen mit seinem eigenen Bedeutungsstapel assoziiert. Dies vermeidet eine lineare Suche während der Laufzeit, um einen bestimmten Namen zu finden. Es sollte jedoch darauf geachtet werden, dass diese Tabelle ordnungsgemäß gewartet wird.[17] Beachten Sie, dass diese beiden Strategien ein Last-In-First-Out annehmen (LIFO) Bestellung an Bindungen für eine Variable; In der Praxis sind alle Bindungen so geordnet.
Eine noch einfachere Implementierung ist die Darstellung dynamischer Variablen mit einfachen globalen Variablen. Die lokale Bindung wird durchgeführt, indem der ursprüngliche Wert an einem anonymen Ort auf dem Stapel gespeichert wird, der für das Programm unsichtbar ist. Wenn dieser Bindungsbereich endet, wird der ursprüngliche Wert von diesem Ort wiederhergestellt. Tatsächlich entstand der dynamische Bereich auf diese Weise. Frühe Implementierungen von LiSP verwendeten diese offensichtliche Strategie zur Implementierung lokaler Variablen, und die Praxis überlebt in einigen noch verwendeten Dialekten, wie z. B. GNU EMACS LISP. Der lexikalische Bereich wurde später in LISP eingeführt. Dies entspricht dem obigen flachen Bindungsschema, mit der Ausnahme, dass die zentrale Referenztabelle einfach der globale Variablenbindungskontext ist, in dem die aktuelle Bedeutung der Variablen ihr globaler Wert ist. Die Aufrechterhaltung globaler Variablen ist nicht komplex. Beispielsweise kann ein Symbolobjekt einen speziellen Slot für seinen globalen Wert haben.
Der dynamische Umfang bietet eine hervorragende Abstraktion für Thread-lokaler Speicher, aber wenn es so verwendet wird, kann es nicht auf dem Speichern und Wiederherstellen einer globalen Variablen beruhen. Eine mögliche Implementierungsstrategie besteht darin, dass jede Variable einen Thread-lokalen Schlüssel hat. Wenn auf die Variable zugegriffen wird, wird der Thread-lokale Schlüssel zum Zugriff auf den Thread-Lokal-Speicherort verwendet (nach Code, der vom Compiler generiert wird und die weiß, welche Variablen dynamisch sind und welche lexikalisch sind). Wenn der Thread-lokale Schlüssel für den aufrufenden Thread nicht vorhanden ist, wird der globale Standort verwendet. Wenn eine Variable lokal gebunden ist, wird der vorherige Wert an einem versteckten Ort auf dem Stapel gespeichert. Der Thread-lokale Speicher wird unter dem Schlüssel der Variablen erstellt, und der neue Wert wird dort gespeichert. Weitere verschachtelte Überschreibungen der Variablen in diesem Thread speichern und wiederherstellen Sie diesen Thread-lokalen Standort einfach. Wenn der Kontext der anfänglichen, äußersten Überschreibung endet, wird der Thread-lokale Schlüssel gelöscht und die globale Version der Variablen erneut auf diesen Thread ausgesetzt.
Mit Referenztransparenz Der dynamische Bereich ist nur auf den Argumentstapel der aktuellen Funktion beschränkt und fällt mit dem lexikalischen Bereich zusammen.
Makroerweiterung
In modernen Sprachen, Makroerweiterung in einem Präprozessor ist ein zentrales Beispiel für den dynamischen Bereich von facto. Die Makrosprache selbst transformiert nur den Quellcode, ohne Namen zu beheben. Da die Erweiterung jedoch vorhanden ist, werden die Namen im erweiterten Text aufgelöst (insbesondere freie Variablen), sie werden auf der Grundlage der Erweiterung aufgelöst (lose erweitert (locker "genannt"), als ob der dynamische Umfang auftritt.
Das C Präprozessor, benutzt für Makroerweiterunghat de facto dynamischer Umfang, da er nicht für sich genannt wird und unabhängig davon ist, wo das Makro definiert ist. Zum Beispiel das Makro:
#define add_a (x) x + a
wird erweitert, um hinzuzufügen a
an die bestandene Variable, wobei dieser Name erst später vom Compiler aufgelöst wurde, basierend auf dem Makro FÜGE HINZU EIN
wird "genannt" (richtig, erweitert). Richtig, der C -Präprozessor tut nur lexikalische Analyse, Erweiterung des Makros während der Tokenisierungsphase, aber nicht in einen Syntaxbaum analysiert oder die Namensauflösung erledigt.
Zum Beispiel im folgenden Code der Name a
Im Makro wird (nach Expansion) an der lokalen Variablen an der Erweiterungsstelle aufgelöst:
#define add_a (x) x + a Leere füge eins hinzu(int *x) { Const int a = 1; *x = FÜGE HINZU EIN(*x); } Leere add_two(int *x) { Const int a = 2; *x = FÜGE HINZU EIN(*x); }
Qualifizierte Namen
Wie wir gesehen haben, besteht einer der Hauptgründe für den Umfang darin, dass es dazu beiträgt, Namenskollisionen zu verhindern, indem identische Namen auf unterschiedliche Dinge beziehen, mit der Einschränkung, dass die Namen separate Bereiche haben müssen. Manchmal ist diese Einschränkung unpraktisch; Wenn viele verschiedene Dinge während eines Programms zugänglich sein müssen, benötigen sie im Allgemeinen alle Namen mit globalem Umfang, sodass verschiedene Techniken erforderlich sind, um Namenskollisionen zu vermeiden.
Um dies anzugehen, bieten viele Sprachen Mechanismen zur Organisation globaler Namen. Die Details dieser Mechanismen und die verwendeten Begriffe hängen von der Sprache ab; Die allgemeine Idee ist jedoch, dass eine Gruppe von Namen selbst einen Namen erhalten kann - ein Präfix - und bei Bedarf eine Entität von a qualifizierter Name bestehend aus dem Namen plus dem Präfix. Normalerweise haben solche Namen in gewissem Sinne zwei Sätze von Scopes: einen Bereich (normalerweise der globale Bereich), in dem der qualifizierte Name sichtbar ist, und einen oder mehrere schmalere Bereiche, in denen die uneingeschränkter Name (ohne das Präfix) ist ebenfalls sichtbar. Und normalerweise können diese Gruppen selbst in Gruppen organisiert werden; Das heißt, sie können sein verschachtelt.
Obwohl viele Sprachen dieses Konzept unterstützen, variieren die Details stark. Einige Sprachen haben Mechanismen, wie z. Namespaces in C ++ und C#, die fast ausschließlich dazu dienen, globale Namen in Gruppen zu organisieren. Andere Sprachen haben Mechanismen, wie z. Pakete in Ada und Strukturen in Standard mlDies kombiniert dies mit dem zusätzlichen Zweck, einige Namen nur für andere Mitglieder ihrer Gruppe sichtbar zu sein. Und objektorientierte Sprachen erlauben oft Klassen oder Singleton-Objekte, diesen Zweck zu erfüllen (ob sie sie Auch einen Mechanismus haben, für den dies der Hauptzweck ist). Darüber hinaus verschmelzen Sprachen diese Ansätze oft; zum Beispiel, Perl'S-Pakete ähneln weitgehend den Namespaces von C ++, doppelt jedoch optional als Klassen für objektorientierte Programmierung. und Java organisiert seine Variablen und Funktionen in Klassen, organisiert aber diese Klassen in ADA-ähnlichen Paketen.
Nach Sprache
Umfangsregeln für repräsentative Sprachen folgen.
C
In C ist der Umfang traditionell als bekannt als Verknüpfung oder Sichtweitebesonders für Variablen. C ist eine lexikalisch abgeschottete Sprache mit globalem Umfang (bekannt als externe Verknüpfung) eine Form des Modulbereichs oder Dateiumfangs (bekannt als interne Verknüpfung) und lokaler Umfang (innerhalb einer Funktion); Innerhalb einer Funktion können Bereiche weiter über Blockbereich verschachtelt werden. Standard C unterstützt jedoch keine verschachtelten Funktionen.
Die Lebensdauer und Sichtbarkeit einer Variablen werden durch ihre bestimmt Speicherklasse. Es gibt drei Arten von Lebensdauern in C: statisch (Programmausführung), automatisch (Blockausführung, auf dem Stapel zugewiesen) und manuell (auf dem Haufen zugewiesen). Nur statisch und automatisch werden für Variablen unterstützt und vom Compiler behandelt, während manuell zugewiesene Speicher manuell über verschiedene Variablen hinweg verfolgt werden muss. In C: externer Verknüpfung (global), interner Verknüpfung (grob Datei) und Blockbereich (einschließlich Funktionen) gibt es drei Sichtbarkeitsebenen. Blockbereiche können verschachtelt werden, und es können verschiedene Ebenen der internen Verknüpfung verwendet werden. Interne Verknüpfung in C ist die Sichtbarkeit bei der Übersetzungseinheit Ebene, nämlich eine Quelldatei, nachdem sie von der verarbeitet wurden C Präprozessorinsbesondere einschließlich aller relevanten Einschlüsse.
C -Programme werden als getrennt zusammengestellt Objektdateien, die dann über a in eine ausführbare Datei oder Bibliothek verknüpft werden Linker. Somit wird die Namensauflösung über den Compiler aufgeteilt, der Namen in einer Übersetzungseinheit (lockerer "Kompilierungseinheit" auflöst, dies ist jedoch ordnungsgemäß ein anderes Konzept) und der Linker, der Namen über Übersetzungseinheiten hinweg auflöst. sehen Verknüpfung zur weiteren Diskussion.
In C geben Variablen mit Blockbereich den Kontext ein, wenn sie deklariert werden (nicht oben im Block), aus dem Kontext, wenn eine (nicht Nestnest) Funktion in den Block aufgerufen wird, kehren Sie in den Kontext zurück, wenn die Funktion zurückgibt. und gehen Sie am Ende des Blocks aus dem Kontext. Bei automatischen lokalen Variablen werden sie auch in Bezug auf die Erklärung zugewiesen und am Ende des Blocks verhandelt, während sie für statische lokale Variablen bei der Programminitialisierung zugewiesen und bei der Programmabgabe verhandelt werden.
Das folgende Programm zeigt eine Variable, wobei der Blockbereich durch den Block teilweise in den Kontext kommt und dann den Kontext verlässt (und tatsächlich mit dem Ende des Blocks beendet wird):
#enthalten int hauptsächlich(Leere) { verkohlen x = 'm'; printf("%c\n", x); { printf("%c\n", x); verkohlen x = 'b'; printf("%c\n", x); } printf("%c\n", x); }
Das Programm gibt aus:
m m b m
Es gibt andere Umfangsniveaus in C.[18] In einem Funktionsprototyp verwendete Variablennamen haben die Sichtbarkeit des Funktionsprototyps und beenden den Kontext am Ende des Funktionsprototyps. Da der Name nicht verwendet wird, ist dies nicht nützlich für die Zusammenstellung, kann jedoch für die Dokumentation nützlich sein. Etikettennamen für die GOTO -Anweisung haben Funktionsumfang, während Fallbezeichnungsnamen für Anweisungen wechseln Blockbereich (den Block des Schalters) haben.
C ++
Alle Variablen, die wir in einem Programm verwenden möchten, müssen in einem früheren Punkt im Code mit seinem Typ -Spezifizierer deklariert worden sein, wie wir es im vorherigen Code zu Beginn des Hauptkörpers der Funktionsmainie getan haben, als wir erklärten, dass a. B und das Ergebnis waren vom Typ int. Eine Variable kann entweder globaler oder lokaler Bereiche sein. Eine globale Variable ist eine Variable, die im Hauptteil des Quellcode außer allen Funktionen deklariert ist, während eine lokale Variable im Körper einer Funktion oder eines Blocks deklariert ist.
Moderne Versionen ermöglichen verschachtelter lexikalischer Bereich.
Schnell
Schnell hat eine ähnliche Regel für Scopes mit C ++, enthält jedoch unterschiedliche Zugriffsmodifikatoren.
Modifikator | Sofortiger Umfang | Datei | Modul/Paket enthalten | Rest der Welt |
---|---|---|---|---|
offen | Ja | Ja | Ja | Ja, erlaubt Unterklasse |
Öffentlichkeit | Ja | Ja | Ja | Ja, die Unterklasse nicht ausmacht |
intern | Ja | Ja | Ja | Nein |
Fileprivat | Ja | Ja | Nein | Nein |
Privatgelände | Ja | Nein | Nein | Nein |
gehen
gehen wird mit Blöcken lexikalisch abgeschrieben.[3]
Java
Java ist lexikalisch abgebildet.
Eine Java -Klasse kann drei Arten von Variablen enthalten:[19]
- Lokale Variablen
- werden in einer Methode oder einem bestimmten Block definiert. Diese Variablen sind lokal dort, wo sie definiert wurden, und niedrigere Ebenen. Beispielsweise kann eine Schleife innerhalb einer Methode die lokalen Variablen dieser Methode verwenden, aber nicht umgekehrt. Die Variablen der Schleife (lokal zu dieser Schleife) werden zerstört, sobald die Schleife endet.
- Mitgliedsvariablen
- auch genannt Felder sind Variablen innerhalb der Klasse außerhalb jeder Methode deklariert. Standardmäßig sind diese Variablen für alle Methoden innerhalb dieser Klasse und auch für alle Klassen im Paket verfügbar.
- Parameter
- sind Variablen in Methodendeklarationen.
Im Allgemeinen definiert eine Reihe von Klammern einen bestimmten Bereich, aber Variablen auf der oberen Ebene innerhalb einer Klasse können sich in ihrem Verhalten abhängig von den in ihrer Definition verwendeten Modifikator -Schlüsselwörtern unterscheiden. Die folgende Tabelle zeigt den Zugang zu Mitgliedern, die von jedem Modifikator zulässig sind.[20]
Modifikator | Klasse | Paket | Unterklasse | Welt |
---|---|---|---|---|
Öffentlichkeit | Ja | Ja | Ja | Ja |
geschützt | Ja | Ja | Ja | Nein |
(kein Modifikator) | Ja | Ja | Nein | Nein |
Privatgelände | Ja | Nein | Nein | Nein |
JavaScript
JavaScript hat einfach Umfangsregeln,[21] Die Regeln für variable Initialisierung und Namensauflösung können jedoch Probleme verursachen, und die weit verbreitete Verwendung von Schließungen für Rückrufe bedeutet, dass der lexikalische Kontext einer Funktion, wenn sie definiert sind (die für die Auflösung von Namen verwendet wird), sehr unterschiedlich vom lexikalischen Kontext unterscheiden kann, wenn es aufgerufen wird (welche ist irrelevant für die Namensauflösung). JavaScript -Objekte haben eine Namensauflösung für Eigenschaften, dies ist jedoch ein separates Thema.
JavaScript hat einen lexikalischen Umfang [22] Auf Funktionsebene verschachtelt, wobei der globale Kontext der äußerste Kontext ist. Dieser Bereich wird für sowohl Variablen als auch für Funktionen verwendet (Bedeutung Funktionserklärung im Gegensatz zu Variablen von Funktionstyp).[23] Blockenziele mit dem blockieren Lassen
und Const
Schlüsselwörter sind serienmäßig seitdem ECMaskript 6. Blockbereich kann erzeugt werden, indem der gesamte Block in eine Funktion einwickelt und dann ausführt. Dies ist als die bekannt Sofortiger Funktionsausdruck (Iife) Muster.
Während der JavaScript-Bereich einfach ist, sind die zugehörigen Regeln für Initialisierung und Namensauflösung eine Ursache für Verwirrung. Erstens ist die Zuordnung zu einem Namen, der nicht in SCOPE -Standardeinstellungen zu erstellen ist, eine neue globale Variable, nicht eine lokale. Zweitens muss man die neue lokale Variable erstellen, die man verwenden muss var
Stichwort; Die Variable wird dann oben in der Funktion mit Wert erstellt nicht definiert
und der Variablen wird ihr Wert zugewiesen, wenn der Zuweisungsausdruck erreicht ist:
- Eine Variable mit einer Initialisierer wird der Wert seiner zugewiesen Zuweisungsexpression wenn der Variablestatement wird ausgeführt, nicht wenn die Variable erstellt wird.[24]
Dies ist bekannt als als Variable Hebezeuge[25]- Die Erklärung, aber nicht die Initialisierung, wird an die Spitze der Funktion gehoben. Drittens Zugriff auf Variablen vor der Initialisierung ergibt nicht definiert
, anstelle eines Syntaxfehlers. Viertens werden für Funktionserklärungen die Deklaration und die Initialisierung im Gegensatz zur variablen Initialisierung an der Spitze der Funktion gehoben. Zum Beispiel erzeugt der folgende Code einen Dialog mit Ausgabe nicht definiertDa die lokale Variablenerklärung gehisst wird, beschattet die globale Variable, die Initialisierung jedoch nicht, sodass die Variable bei Verwendung nicht definiert ist:
a = 1; Funktion f() { Alarm(a); var a = 2; } f();
Da Funktionen erstklassige Objekte in JavaScript sind und häufig als Rückrufe zugeordnet oder aus Funktionen zurückgegeben werden. Wenn eine Funktion ausgeführt wird, hängt die Namensauflösung davon ab, wo sie ursprünglich definiert wurde (der lexikalische Kontext der Definition), nicht die lexikalischen Kontext oder Ausführungskontext, in dem er aufgerufen wird. Die verschachtelten Bereiche einer bestimmten Funktion (vom globalsten bis zu den lokalen) in JavaScript, insbesondere eines Verschlusses, der als Rückruf verwendet wird, werden manchmal als die bezeichnet BereichsketteAnalogie zur Prototypkette eines Objekts.
Schließungen Kann in JavaScript unter Verwendung verschachtelter Funktionen erzeugt werden, da Funktionen erstklassige Objekte sind.[26] Die Rückgabe einer verschachtelten Funktion aus einer umschließenden Funktion enthält die lokalen Variablen der einschließenden Funktion als (nicht lokaler) lexikaler Kontext der zurückgegebenen Funktion, wodurch ein Verschluss führt. Zum Beispiel:
Funktion Newcounter() { // Geben Sie einen Zähler zurück, der auf Anruf erhöht ist (ab 0) // und was seinen neuen Wert zurückgibt var a = 0; var b = Funktion() { a++; Rückkehr a; }; Rückkehr b; } c = Newcounter(); Alarm(c() + '' ' + c()); // Ausgibt "1 2"
Verschlüsse werden häufig in JavaScript verwendet, da für Rückrufe verwendet werden. In der Tat erzeugt jede Ansammlung einer Funktion im lokalen Kontext als Rückruf oder Rückgabe von einer Funktion einen Verschluss, wenn es im Funktionsorganisation ungebundene Variablen gibt (mit dem Kontext des Verschlusses basierend auf den verschachtelten Gebieten des aktuellen lexikalischen Kontextes , oder "Scope -Kette"); Dies kann zufällig sein. Beim Erstellen eines auf Parametern basierenden Rückrufs müssen die Parameter in einem Schluss gespeichert werden. Andernfalls erzeugt sie versehentlich eine Schließung, die sich auf die Variablen im umschließenden Kontext bezieht, der sich ändern kann.[27]
Die Namensauflösung der Eigenschaften von JavaScript -Objekten basiert auf der Vererbung im Prototypbaum - ein Weg zur Wurzel im Baum wird als a genannt Prototypkette- und ist von der Namensauflösung von Variablen und Funktionen getrennt.
Lispeln
Lispeln Dialekte haben verschiedene Regeln für den Umfang.
Der ursprüngliche LISP verwendet dynamische Umfang; es war Planen, inspiriert von AlgolDas führte der Lisp -Familie den statischen (lexikalischen) Umfang ein.
MacLisp standardmäßig verwendete dynamische Bereiche im Interpreter und im lexikalischen Bereich im kompilierten Code standardmäßig, obwohl kompilierter Code mithilfe von dynamischen Bindungen zugreifen kann BESONDERE
Deklarationen für bestimmte Variablen.[28] Jedoch, MacLisp behandelte lexikalische Bindung mehr als Optimierung als man in modernen Sprachen erwarten würde, und es kam nicht mit dem einher Schließung Feature Man könnte in modernen LISPs einen lexikalischen Bereich erwarten. Eine separate Operation, *FUNKTION
, war verfügbar, um etwas ungeschickt um ein Teil dieses Problems zu arbeiten.[29]
Common Lisp adoptierte lexikalische Umfang von Planen,[30] so wie ... getan hat Clojure.
ISLISP hat einen lexikalischen Umfang für gewöhnliche Variablen. Es hat auch dynamische Variablen, aber in allen Fällen explizit markiert; Sie müssen durch a definiert werden defdynamisch
besondere Form, gebunden an a Dynamik
besondere Form und von einem expliziten Zugriff auf dynamisch
besondere Form.[31]
Einige andere LISP -Dialekte wie EMACS LispVerwenden Sie standardmäßig den dynamischen Bereich. EMACS LISP ist jetzt einen lexikalischen Umfang pro Puffer erhältlich.[32]
Python
Für Variablen verfügt Python über Funktionsumfang, Modulbereich und globaler Umfang. Namen geben den Kontext zu Beginn eines Umfangs (Funktion, Modul oder globaler Umfang) ein und beenden den Kontext, wenn eine nicht verstärkte Funktion aufgerufen wird oder das Zielfernrohr endet. Wenn vor der variablen Initialisierung ein Name verwendet wird, erhöht dies eine Laufzeitausnahme. Wenn einfach auf eine Variable zugegriffen wird (nicht zugewiesen), folgt die Namensauflösung der LEGB-Regel (lokal, eingeschlossen, global, integriert), die Namen in den engsten relevanten Kontext auflöst. Wenn jedoch eine Variable zugeordnet ist, ist es standardmäßig, eine Variable zu deklarieren, deren Umfang zu Beginn der Ebene (Funktion, Modul oder global) und nicht an der Zuordnung beginnt. Beide Regeln können mit a überschrieben werden global
oder nicht lokal
(In Python 3) Deklaration vor der Verwendung, die den Zugriff auf globale Variablen ermöglicht, auch wenn eine maskierende nichtlokale Variable vorliegt und globale oder nichtlokale Variablen zugewiesen wird.
Als einfaches Beispiel löst eine Funktion eine Variable zum globalen Bereich:
>>> def f(): ... drucken(x) ... >>> x = "global" >>> f() global
Beachten Sie, dass x
ist vorher definiert f
wird genannt, so dass kein Fehler aufgeworfen wird, obwohl es nach seiner Referenz in der Definition von definiert ist f
. Lexikalisch ist das a Vorwärtsreferenz, was in Python erlaubt ist.
Hier schafft Zuweisung eine neue lokale Variable, die den Wert der globalen Variablen nicht ändert:
>>> def f(): ... x = "f" ... drucken(x) ... >>> x = "global" >>> drucken(x) global >>> f() f >>> drucken(x) global
Die Zuordnung zu einer Variablen innerhalb einer Funktion führt dazu, dass sie für die Funktion lokal deklariert wird. Daher ist der Umfang die gesamte Funktion, und die Verwendung dieser Zuweisung erhöht daher einen Fehler. Dies unterscheidet sich von C, wo der Umfang der lokalen Variablen bei seiner Erklärung beginnt. Dieser Code erhöht einen Fehler:
>>> def f(): ... drucken(x) ... x = "f" ... >>> x = "global" >>> f() Traceback (letzte Anruflast): Datei "<stdin>", Linie 1, in Datei "<stdin>", Linie 2, in f Ungebundener ERROR: Lokale Variable 'x', auf die vor der Zuordnung verwiesen wird
Die Standardregeln für die Standardname -Auflösung können mit dem überschrieben werden global
oder nicht lokal
(in Python 3) Schlüsselwörter. Im folgenden Code die global x
Erklärung in g
bedeutet, dass x
Auflösungen in die globale Variable. Es kann somit zugegriffen werden (wie bereits definiert wurde), und die Zuweisung der globalen Variablen zugewiesen, anstatt eine neue lokale Variable zu deklarieren. Beachten Sie, dass nein global
Erklärung ist in erforderlich f
- Da es der Variablen nicht zugewiesen wird, wird es standardmäßig auf die globale Variable gelöst.
>>> def f(): ... drucken(x) ... >>> def g(): ... global x ... drucken(x) ... x = "g" ... >>> x = "global" >>> f() global >>> g() global >>> f() g
global
kann auch für verschachtelte Funktionen verwendet werden. Neben der Zuordnung zu einer globalen Variablen wie in einer nicht ohnmächtigenden Funktion kann dies auch verwendet werden, um in Gegenwart einer nichtlokalen Variablen auf die globale Variable zuzugreifen:
>>> def f(): ... def g(): ... global x ... drucken(x) ... x = "f" ... g() ... >>> x = "global" >>> f() global
Für verschachtelte Funktionen gibt es auch die nicht lokal
Deklaration für die Zuweisung einer nichtlokalen Variablen, ähnlich wie bei der Verwendung global
in einer uned unfensierten Funktion:
>>> def f(): ... def g(): ... nicht lokal x # Python 3 nur ... x = "g" ... x = "f" ... g() ... drucken(x) ... >>> x = "global" >>> f() g >>> drucken(x) global
R
R ist eine lexikalisch abgestellte Sprache, im Gegensatz zu anderen Implementierungen von S wobei die Werte freier Variablen durch eine Reihe globaler Variablen bestimmt werden, während sie in R durch den Kontext bestimmt werden, in dem die Funktion erstellt wurde.[33] Auf die Umfangskontexte kann mit einer Vielzahl von Funktionen zugegriffen werden (wie z. parent.frame ()
) die die Erfahrung des dynamischen Umfangs simulieren kann, falls der Programmierer wünscht.
Es gibt keinen Blockbereich:
a <- 1 { a <- 2 } Botschaft(a) ## 2
Funktionen haben Zugriff auf den Umfang, in dem sie erstellt wurden:
a <- 1 f <- Funktion() { Botschaft(a) } f() ## 1
Variablen, die innerhalb einer Funktion erstellt oder geändert wurden, bleiben dort:
a <- 1 f <- Funktion() { Botschaft(a) a <- 2 Botschaft(a) } f() ## 1 ## 2 Botschaft(a) ## 1
Variablen, die innerhalb eines Funktions erstellt oder geändert wurden, bleiben dort, es sei denn, die Zuordnung zum Einschließungsbereich wird ausdrücklich angefordert:
a <- 1 f <- Funktion() { Botschaft(a) a <<- 2 Botschaft(a) } f() ## 1 ## 2 Botschaft(a) ## 2
Obwohl R standardmäßig einen lexikalischen Bereich hat, können Funktionsbereiche geändert werden:
a <- 1 f <- Funktion() { Botschaft(a) } my_env <- new.env() my_env$a <- 2 f() ## 1 Umgebung(f) <- my_env f() ## 2
Siehe auch
- Verschluss (Informatik)
- Globale Variable
- Lokale Variable
- Lassen Sie den Ausdruck
- Nicht-lokale Variable
- Namebindung
- Namensauflösung (Programmiersprachen)
- Variablen (Umfang und Ausmaß)
- Informationen verstecken sich
- Sofortige Ausdrücke von Funktionsausdrücken In JavaScript
- Objektlebensdauer
Anmerkungen
- ^ Sehen Definition für die Bedeutung von "Scope" versus "Kontext".
- ^ "Dynamic Scope" Basen Name Auflösung auf Ausmaß (Lebensdauer), nicht Umfangund so ist formal ungenau.
- ^ Zum Beispiel die Jinja Template Engine für Python standardmäßig verwendet sowohl der lexikalische Bereich (für Importe) als auch dynamische Umfang (für Include) und ermöglicht es, das Verhalten mit Schlüsselwörtern anzugeben. sehen Kontextverhalten importieren.
- ^ "Namensauflösung" und "Name Bindung" sind weitgehend synonym; eng gesehen bestimmt "Auflösung", auf welchen Namen eine bestimmte Verwendung eines Namens bezieht, ohne ihn mit irgendeiner Bedeutung zu assoziieren, wie Abstract Syntax höherer Ordnung, während "Bindung" den Namen mit einer tatsächlichen Bedeutung verbindet. In der Praxis werden die Begriffe austauschbar verwendet.
- ^ Zum selbstmodifizierender Code Der lexikalische Kontext selbst kann sich während der Laufzeit ändern.
- ^ Im Gegensatz dazu ist *"Der Kontext eines Namensbindung", *"Ein Name, der in den Geltungsbereich kommt" oder *"Ein Name, der aus dem Umfang des Geltungsbereichs geht", sind alle falsch - eine Namensbindung hat den Geltungsbereich, während ein Teil eines Programms einen Kontext hat.
Verweise
- ^ "Bericht über das algorithmische Sprachalgol 60", 2.7. Mengen, Arten und Bereiche
- ^ WG14 N1256 (2007 aktualisierte Version der C99 Standard), 6.2.1 Bereiche von Identifikatoren, 2007-09-07
- ^ a b Die GO -Programmiersprachenspezifikation: Erklärungen und Umfang, Version vom 13. November 2013
- ^ a b c Borning A. CSE 341 - lexikalischer und dynamischer Scoping. Universität von Washington.
- ^ Crockford, Douglas. "Codekonventionen für die JavaScript -Programmiersprache". Abgerufen 2015-01-04.
- ^ Backus, J. W.; Wegstein, J. H.; Van Wijngaarden, A.; Woodger, M.; Bauer, F. L.; Green, J.; Katz, C.; McCarthy, J.; Perlis, A. J.; Rutishauser, H.; Samelson, K.; Vauquois, B. (1960). "Bericht über das algorithmische Sprachalgol 60". Kommunikation der ACM. 3 (5): 299. doi:10.1145/367236.367262. S2CID 278290.
- ^ "Funktionen - JavaScript: MDN".
In einer Funktion definierte Variablen können nicht von überall außerhalb der Funktion zugegriffen werden, da die Variable nur im Bereich der Funktion definiert ist. Eine Funktion kann jedoch auf alle Variablen und Funktionen zugreifen, die in dem Bereich definiert sind, in dem sie definiert ist.
- ^ "Programmiersprache Pragmatik", Leblank-Cook-Symboltabelle
- ^ "Eine Symbol Tabelle Abstraktion zur Implementierung von Sprachen mit explizit", LeBlank-Cook, 1983
- ^ Louis Steele, Guy (August 1982). "Ein Überblick über Common Lisp". LFP '82: Verfahren des ACM -Symposiums von 1982 über Lisp und funktionelle Programmierung: 98–107. doi:10.1145/800068.802140. ISBN 0897910826. S2CID 14517358.
- ^ Joel, Moses (Juni 1970). "Die Funktion der Funktion in Lisp". MIT AI Memo 199. MIT künstliche Intelligenzlabor.
- ^ Steele, Guy Lewis Jr.; Sussman, Gerald Jay (Mai 1978). "Die Kunst des Dolmetschers; oder der Modularitätskomplex (Teile Null, eins und zwei)". MIT AI Memo 453. MIT künstliche Intelligenzlabor.
- ^ Shivers, Olin. "Geschichte von T". Paul Graham. Abgerufen 5. Februar 2020.
- ^ Steele, Guy Lewis Jr. (Mai 1978). "Kaninchen: Ein Compiler für Schema". MIT. HDL:1721.1/6913.
{{}}
: Journal zitieren erfordert|journal=
(Hilfe) - ^ "lexikalischer Bereich",", Computer- und Programmorganisation, Teil 3, p. 18, at Google Bücher, Universität von Michigan. Technische Sommerkonferenzen, 1967
- ^ "lexikaler Scoping",", Projekt Mac Progress Report, Band 8, p. 80, at Google Bücher, 1970.
- ^ a b Scott 2009, 3.4 Implementierung des Umfangs, p. 143.
- ^ "Zielfernrohr",", XL C/C ++ V8.0 für Linux, IBM
- ^ "Deklarieren von Mitgliedsvariablen (die Java ™ -Tutorials> Lernen der Java -Sprache> Klassen und Objekte)". docs.oracle.com. Abgerufen 19. März 2018.
- ^ "Zugriff auf Mitglieder einer Klasse (die Java ™ -Tutorials> Lernen der Java -Sprache> Klassen und Objekte)". docs.oracle.com. Abgerufen 19. März 2018.
- ^ "Alles, was Sie über den variablen JavaScript -Bereich wissen müssen",", Saurab Parakh, Codierung ist cool, 2010-02-08
- ^ "Annotated ES5". Es5.github.io. Abgerufen 19. März 2018.
- ^ "Funktionen". MDN Web Docs. Abgerufen 19. März 2018.
- ^ "12.2 Variable Anweisung", Annotated ECMascript 5.1, zuletzt aktualisiert: 2012-05-28
- ^ "JavaScript -Scoping und Hebel",", Ben Cherry, Ausreichend gut, 2010-02-08
- ^ JavaScript -Schließungen, Richard Cornford. März 2004
- ^ "Erklären von JavaScript -Umfang und Schließungen", Robert Nyman, 9. Oktober 2008
- ^ Pitman, Kent (16. Dezember 2007). "Das überarbeitete MacLisp -Handbuch (The Pitmanual), Sonntagmorgenausgabe". Maclisp.info. Hypermeta Inc. Deklarationen und der Compiler, Konzept "Variablen". Abgerufen 20. Oktober, 2018.
Wenn die zugebende Variable als besonders deklariert wurde, wird die Bindung als Code zusammengestellt, um die Art und Weise zu imitieren, wie der Interpreter Variablen bindet
- ^ Pitman, Kent (16. Dezember 2007). "Das überarbeitete MacLisp -Handbuch (The Pitmanual), Sonntagmorgenausgabe". Maclisp.info. Hypermeta Inc. Der Bewerter, Sonderform
*FUNKTION
. Abgerufen 20. Oktober, 2018.*FUNKTION
soll helfen, die zu lösen “Funarg Problem, “Es funktioniert jedoch nur in einigen einfachen Fällen. - ^ Pitman, Kent; et al. (Webbed Version von ANSI Standard X3.226-1994) (1996). "Common Lisp hyperspec". Lispworks.com. Lispworks Ltd. 1.1.2 Geschichte. Abgerufen 20. Oktober, 2018.
MACLISP verbesserte den LISP 1,5-Begriff spezieller Variablen ... Die Haupteinflüsse auf das gemeinsame Lisp-Lisp-Maschinenlispen, Maclisp, NIL, S-1 Lisp, Spice Lisp und Schema.
- ^ "Programmiersprache ISLISP, ISLISP -Arbeitsentwurf 23.0" (PDF). ISLISP.Info. 11.1 Das lexikalische Prinzip. Abgerufen 20. Oktober, 2018.
Dynamische Bindungen werden durch einen separaten Mechanismus hergestellt und zugegriffen (d. H.,,
defdynamisch
,Dynamik
, unddynamisch
). - ^ "Lexikalische Bindung". Emacswiki. Abgerufen 20. Oktober, 2018.
EMACS 24 verfügt über eine optionale lexikalische Bindung, die per Puffer aktiviert werden kann.
- ^ "R FAQ". cran.r-project.org. Abgerufen 19. März 2018.
- Abelson, Harold; Sussman, Gerald Jay; Sussman, Julie (1996) [1984]. Struktur und Interpretation von Computerprogrammen. Cambridge, MA: MIT Press. ISBN 0-262-51087-1.
- Scott, Michael L. (2009) [2000]. Programmiersprache Pragmatik (Dritter Aufl.). Morgan Kaufmann Publishers. ISBN 978-0-12-374514-9.
- Kapitel 3: Namen, Bereiche und Bindungen, S. 111–174
- Abschnitt 13.4.1: Skriptsprachen: Innovative Funktionen: Namen und Scopes, S. 691–699