Czym jest zakres leksykalny?

Czy ktoś mógłby mi dać krótkie wprowadzenie do zakresu leksykalnego?

Author: Adrian Heine, 2009-06-26

12 answers

Rozumiem je na przykładach. :)

Po pierwsze, zakres leksykalny (zwany także zakresem statycznym), w składni podobnej do C:
void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}
Każdy wewnętrzny poziom ma dostęp do zewnętrznych poziomów.

Istnieje inny sposób, zwany Dynamic Scope używany przez pierwszą implementację Lispu, znowu w C-podobnej składni:

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Tutaj fun może uzyskać dostęp x W dummy1 lub dummy2, lub dowolna x W dowolnej funkcji, która wywołuje fun z x zadeklarowaną w niej.

dummy1();

Drukuje 5,

dummy2();

Wydrukuje 10.

Pierwszy nazywa się statycznym, ponieważ można go wydedukować w czasie kompilacji, drugi nazywa się dynamicznym, ponieważ zewnętrzny zakres jest dynamiczny i zależy od wywołania łańcuchowego funkcji.

[15]}statyczne przeszukiwanie jest łatwiejsze dla oka. Większość języków poszła w ten sposób w końcu nawet Lisp(może robić oba, prawda?). Dynamiczny zakres jest jak przekazywanie referencji wszystkich zmiennych do wywołanej funkcji.

Przykład dlaczego kompilator może nie wydedukować zewnętrznego zakresu dynamicznego funkcji, rozważ nasz ostatni przykład, jeśli napiszemy coś takiego:

if(/* some condition */)
    dummy1();
else
    dummy2();

Łańcuch wywołania zależy od warunku czasu wykonania. Jeśli jest to prawda, to łańcuch wywołania wygląda następująco:

dummy1 --> fun()

Jeśli warunek jest fałszywy:

dummy2 --> fun()

Zewnętrzny zakres fun w obu przypadkach to rozmówca plus rozmówca rozmówcy i tak dalej .

Wystarczy wspomnieć, że język C nie pozwala na zagnieżdżanie funkcji ani dynamiczne namierzam.

 566
Author: AraK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-29 10:30:38

Spróbujmy najkrótszej możliwej definicji:

Zakres leksykalny określa sposób rozwiązywania nazw zmiennych w funkcjach zagnieżdżonych: funkcje wewnętrzne zawierają zakres funkcji nadrzędnych, nawet jeśli funkcja nadrzędna zwróciła .

To wszystko! Aby pomóc sobie zrozumieć, co to oznacza, napisałem dogłębny post na blogu o zakres funkcji i leksykalny zakres w JavaScript, który można znaleźć tutaj . Może to komuś pomoże else too.

 207
Author: Pierre Spring,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-08 12:17:32

Scope definiuje obszar, w którym dostępne są funkcje, zmienne i takie. Dostępność zmiennej na przykład jest zdefiniowana w jej kontekście, powiedzmy funkcji, pliku lub obiektu, w którym są zdefiniowane. Zwykle nazywamy te zmienne lokalne.

Część leksykalna oznacza, że zakres można odczytać z kodu źródłowego.

Zakres leksykalny jest również znany jako zakres statyczny.

Zakres dynamiczny definiuje zmienne globalne, które mogą być wywoływane lub odwoływane z dowolnego miejsca po zdefiniowaniu. Czasami nazywane są zmiennymi globalnymi, chociaż zmienne globalne w większości języków programowania mają zakres leksykalny. Oznacza to, że można wyciągnąć z odczytu kodu, że zmienna jest dostępna w tym kontekście. Być może trzeba postępować zgodnie z klauzulą uses lub includes, aby znaleźć instatiację lub definicję, ale kod / kompilator wie o zmiennej w tym miejscu.

W dynamicznym przeszukiwaniu, natomiast najpierw wyszukujesz w funkcji lokalnej, następnie przeszukujesz funkcję, która wywołała funkcję lokalną, następnie przeszukujesz funkcję, która wywołała tę funkcję, i tak dalej, w górę stosu wywołań. "Dynamiczny" odnosi się do zmiany, w tym, że stos wywołań może być inny za każdym razem, gdy dana funkcja jest wywoływana, a więc funkcja może trafić różne zmienne w zależności od tego, skąd jest wywoływana. (Zobacz tutaj )

Aby zobaczyć ciekawy przykład zakresu dynamicznego zobacz tutaj .

Szczegółowe informacje można znaleźć w tutaj i tutaj .

Niektóre przykłady w Delphi / Object Pascal

Delphi ma zakres leksykalny.

unit Main;
uses aUnit;  // makes available all variables in interface section of aUnit

interface

  var aGlobal: string; // global in the scope of all units that use Main;
  type 
    TmyClass = class
      strict private aPrivateVar: Integer; // only known by objects of this class type
                                    // lexical: within class definition, 
                                    // reserved word private   
      public aPublicVar: double;    // known to everyboday that has access to a 
                                    // object of this class type
    end;

implementation

  var aLocalGlobal: string; // known to all functions following 
                            // the definition in this unit    

end.

Najbliższe Delphi do zakresu dynamicznego jest para funkcji RegisterClass () / GetClass (). Po jego użyciu patrz tutaj .

Załóżmy, że czas wywołania RegisterClass ([TmyClass]) do zarejestrowania określonej klasy nie może być przewidywany przez odczytanie kodu (zostanie wywołany metodą kliknięcia przycisku wywołaną przez użytkownika), wywołanie kodu GetClass ('TmyClass') otrzyma wynik lub nie. Wywołanie RegisterClass () nie musi znajdować się w leksykalnym zakresie jednostki używającej GetClass ();

Inną możliwością dla zakresu dynamicznego są metody anonimowe (zamknięcia) w Delphi 2009, ponieważ znają zmienne ich funkcji wywołującej. Nie podąża on stamtąd rekurencyjnie ścieżką wywołania i dlatego nie jest w pełni dynamiczny.

 34
Author: Ralph M. Rickenbach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:26

Zakres leksykalny (aka statyczny) odnosi się do określenia zakresu zmiennej wyłącznie na podstawie jej pozycji w korpusie tekstowym kodu. Zmienna zawsze odnosi się do środowiska najwyższego poziomu. Dobrze jest to rozumieć w relacji do zakresu dynamicznego.

 33
Author: Evan Meagher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-26 05:14:41
var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

Powyższy kod zwróci "I am just a local". Nie powróci "jestem globalny". Ponieważ funkcja func () liczy gdzie jest pierwotnie zdefiniowana, która znajduje się w zakresie funkcji whatismyscope.

Nie będzie przeszkadzał z tego, co jest wywoływane(globalny zakres/z innej funkcji nawet), dlatego wartość global scope I am global nie zostanie wydrukowana.

Nazywa się to skokiem leksykalnym, gdzie " funkcje są wykonywane za pomocą łańcucha zakresu tak było, gdy zostały zdefiniowane " - zgodnie z instrukcją JavaScript Definition Guide.

Zakres leksykalny jest bardzo potężnym pojęciem.

Mam nadzieję, że to pomoże..:)
 32
Author: kta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-14 05:24:38

Uwielbiam w pełni funkcjonalne, agnostyczne odpowiedzi od ludzi takich jak @ Arak. Ponieważ to pytanie zostało oznaczone JavaScript , chciałbym umieścić kilka uwag bardzo specyficznych dla tego języka.

W javascript wybieramy:

  • as-is (no scope adjustment)
  • leksykalny var _this = this; function callback(){ console.log(_this); }
  • bound callback.bind(this)

Warto zauważyć, myślę, że JavaScript tak naprawdę nie ma dynamicznego zakresu . .bind dostosowuje this słowo kluczowe, i to jest blisko, ale technicznie nie to samo.

Oto przykład demonstrujący oba podejścia. Robisz to za każdym razem, gdy podejmujesz decyzję o zakresie wywołań zwrotnych, więc dotyczy to obietnic, procedur obsługi zdarzeń i innych.

Leksykalne

Oto, co możesz nazwać Lexical Scoping wywołań zwrotnych w JavaScript:

var downloadManager = {
  initialize: function() {
    var _this = this; // Set up `_this` for lexical access
    $('.downloadLink').on('click', function () {
      _this.startDownload();
    });
  },
  startDownload: function(){
    this.thinking = true;
    // request the file from the server and bind more callbacks for when it returns success or failure
  }
  //...
};

Bound

Innym sposobem na scope jest użycie Function.prototype.bind:

var downloadManager = {
  initialize: function() {
    $('.downloadLink').on('click', function () {
      this.startDownload();
    }.bind(this)); // create a function object bound to `this`
  }
//...

Te metody są, o ile wiem, / align = "left" /

 29
Author: SimplGy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-16 22:12:01

Zakres leksykalny: zmienne zadeklarowane poza funkcją są zmiennymi globalnymi i są widoczne wszędzie w programie JavaScript. Zmienne zadeklarowane wewnątrz funkcji mają zakres funkcji i są widoczne tylko dla kodu, który pojawia się wewnątrz tej funkcji.

 9
Author: Stan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-23 10:51:38

IBM definiuje go jako:

Część programu lub jednostki segmentu, w której deklaracja dotyczy. Identyfikator zadeklarowany w rutynie jest znany w tym rutyny i we wszystkich zagnieżdżonych rutynach. Jeśli zagnieżdżona rutyna deklaruje element o tej samej nazwie, element zewnętrzny nie jest dostępny w zagnieżdżona rutyna.

Przykład 1:

function x() {
    /*
    Variable 'a' is only available to function 'x' and function 'y'.
    In other words the area defined by 'x' is the lexical scope of
    variable 'a'
    */
    var a = "I am a";

    function y() {
        console.log( a )
    }
    y();

}
// outputs 'I am a'
x();

Przykład 2:

function x() {

    var a = "I am a";

    function y() {
         /*
         If a nested routine declares an item with the same name,
         the outer item is not available in the nested routine.
         */
        var a = 'I am inner a';
        console.log( a )
    }
    y();

}
// outputs 'I am inner a'
x();
 9
Author: Robert Rocha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 18:54:39

Istnieje ważna część rozmowy wokół leksykalnego i dynamicznego zakresu, której brakuje: proste wyjaśnienie Życia zmiennej o zasięgu-lub Kiedy zmienna może być dostępna.

Zakres dynamiczny tylko bardzo luźno odpowiada zakresowi "globalnemu" w sposób, w jaki tradycyjnie o nim myślimy (powodem, dla którego przywołuję porównanie między tymi dwoma jest to, że zostało już wspomniane {7]} {8]} - i nie podoba mi się szczególnie linked Wyjaśnienie artykułu); najlepiej chyba nie porównywać pomiędzy globalnym i dynamicznym - choć podobno, zgodnie z linkowanym artykułem,"...[it] jest przydatny jako substytut zmiennych o zasięgu globalnym."

Więc, w prostym języku angielskim, jaka jest ważna różnica między tymi dwoma mechanizmami?

Zakres leksykalny został bardzo dobrze zdefiniowany w powyższych odpowiedziach: zmienne o zasięgu leksykalnym są dostępne-lub dostępne-w lokalnym poziom funkcji, w której został zdefiniowany.

Jednakże - ponieważ nie jest to przedmiotem OP - Dynamic scoping nie otrzymał wiele uwagi i uwagi, jaką otrzymał, oznacza, że prawdopodobnie potrzebuje trochę więcej (nie jest to krytyka innych odpowiedzi, ale raczej "och, ta odpowiedź sprawiła, że chcielibyśmy, aby było trochę więcej"). No to jeszcze trochę:

Dynamiczny zakres oznacza, że zmienna jest dostępna dla większego programu w czasie trwania wywołania funkcji - lub, podczas wykonywania funkcji. Naprawdę, Wikipedia faktycznie wykonuje dobrą robotę z wyjaśnieniem różnicy między tymi dwoma. Aby go nie zaciemniać, oto tekst opisujący dynamiczne zakresy:

...[I]N dynamiczny zakres (lub dynamiczny zakres), jeśli zakres nazwy zmiennej jest pewnej funkcji, wówczas jej zakres jest okresem, w którym funkcja jest wykonywana: podczas działania funkcji zmienna nazwa istnieje i jest związana z jej zmienna, ale po funkcji zwraca, nazwa zmiennej nie istnieje.

 4
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:59

Zakres leksykalny oznacza, że funkcja wyszukuje zmienne w kontekście, w którym została zdefiniowana, a nie w zakresie bezpośrednio wokół niej.

Zobacz, jak działa zakres leksykalny w Lispie, jeśli chcesz uzyskać więcej szczegółów. Wybrana odpowiedź Kyle ' a Cronina w dynamicznych i leksykalnych zmiennych w Common Lispie jest o wiele jaśniejsza niż odpowiedzi tutaj.

Przypadkowo dowiedziałem się o tym tylko w klasie Lispu, i zdarza się to również w JS.

Uruchomiłem ten kod w konsola chrome.

// javascript               equivalent Lisp
var x = 5;                //(setf x 5)
console.debug(x);         //(print x)
function print_x(){       //(defun print-x () 
    console.debug(x);     //    (print x)
}                         //)
(function(){              //(let  
    var x = 10;           //    ((x 10))
    console.debug(x);     //    (print x)
    print_x();            //    (print-x)
})();                     //)

Wyjście:

5
10
5 
 2
Author: ratiotile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:37

Zakres leksykalny w Javascript oznacza, że zmienna zdefiniowana poza funkcją może być dostępna wewnątrz innej funkcji zdefiniowanej po deklaracji zmiennej. Ale odwrotnie nie jest prawdą, zmienne zdefiniowane wewnątrz funkcji nie będą dostępne poza tą funkcją.

Pojęcie to jest szeroko stosowane w zamknięciach w Javascript.

Powiedzmy, że mamy poniższy kod.

var x = 2;
var add = function() {
var y = 1;
return x + y;
};

Teraz, gdy wywołasz add () -- > to wyświetli 3.

Więc funkcja add() jest dostęp do zmiennej globalnej x, która jest zdefiniowana przed funkcją metody add. Jest to wywołane ze względu na zakres leksykalny w javascript.

 2
Author: Praveen Kishor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-04 03:29:09

Oto inny punkt widzenia na to pytanie, który możemy uzyskać, cofając się o krok i patrząc na rolę scopingu w szerszych ramach interpretacji (uruchamianie programu). Innymi słowy, wyobraź sobie, że budujesz interpreter (lub kompilator) dla języka i jesteś odpowiedzialny za obliczanie wyjścia, biorąc pod uwagę program i pewne dane wejściowe do niego.

Interpretacja polega na śledzeniu trzech rzeczy:

1) Stan-czyli zmienne i lokalizacje pamięci na stosie i stosie.

2) operacje na tym stanie-czyli każda linijka kodu w twoim programie

3) środowisko, w którym przebiega dana operacja - czyli rzut stanu na operację.

Interpreter zaczyna od pierwszej linii kodu w programie, oblicza jego środowisko, uruchamia linię w tym środowisku i rejestruje jego wpływ na stan programu. Następnie podąża za przepływem sterowania programu, aby wykonać następną linię kodu i powtarza proces do końca programu.

Sposób, w jaki obliczasz środowisko dla dowolnej operacji, opiera się na formalnym zestawie reguł zdefiniowanych przez język programowania. Termin "Wiązanie" jest często używany do opisania mapowania ogólnego stanu programu do wartości w środowisku. Należy zauważyć, że przez" stan ogólny " nie rozumiemy stanu globalnego, ale raczej sumę wszystkich osiągalnych definicji w dowolnym momencie wykonania)

Oto ramy, w których zakres problem jest zdefiniowany. Przejdźmy do następnej części naszych możliwości.

  • jako implementator interpretera, możesz uprościć swoje zadanie, czyniąc środowisko jak najbardziej zbliżone do stanu programu. W związku z tym środowisko linii kodu byłoby po prostu zdefiniowane przez środowisko poprzedniej linii kodu z efektami tej operacji zastosowanymi do niego, niezależnie od tego, czy poprzednia linia była przypisaniem, wywołaniem funkcji, zwrotem z funkcji, czy też struktura sterowania, taka jak pętla while.

Jest to gist Dynamic scoping , w którym środowisko, w którym uruchamiany jest dowolny kod, jest powiązane ze stanem programu zdefiniowanym przez jego kontekst wykonania.

  • lub , można by pomyśleć o programiście używającym Twojego języka i uprościć jego zadanie śledzenia wartości, jakie może przyjąć zmienna. Jest zbyt wiele ścieżek i zbyt wiele złożoności związanych z rozumowaniem o wyniku całkowita egzekucja w przeszłości. zakres leksykalny pomaga w tym poprzez ograniczenie bieżącego środowiska do części stanu zdefiniowanej w bieżący blok, funkcja lub inna jednostka zakresu oraz jego rodzic (tj. blok otaczający bieżący zegar lub funkcja, która wywołała obecną funkcję).

Innymi słowy, z zakres leksykalny środowisko, które widzi każdy kod, jest związane ze stanem związanym z zakresem zdefiniowanym wyraźnie w języku, takich jak blok lub funkcja.

 0
Author: er0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-25 18:46:47