Что такое лексический охват? - PullRequest
604 голосов
/ 26 июня 2009

Может ли кто-нибудь дать мне краткое введение в лексическую область?

Ответы [ 15 ]

628 голосов
/ 26 июня 2009

Я понимаю их на примерах. :)

Во-первых, Lexical Scope (также называемый Static Scope) в C-подобном синтаксисе:

void fun()
{
    int x = 5;

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

Каждый внутренний уровень может получить доступ к своим внешним уровням.

Существует еще один способ, называемый Dynamic Scope, используемый в первой реализации Lisp, снова в C-подобном синтаксисе:

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

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Здесь fun может обращаться к x в dummy1 или dummy2, или к любому x в любой функции, которая вызывает fun с объявленным x.

dummy1();

напечатает 5,

dummy2();

напечатает 10.

Первый называется статическим, потому что он может быть выведен во время компиляции, второй называется динамическим, поскольку внешняя область является динамической и зависит от цепного вызова функций.

Мне легче смотреть на статическое зрение. Большинство языков пошли по этому пути, в конце концов даже Лисп (можно сделать и то, и другое?) Динамическое определение объема похоже на передачу ссылок на все переменные в вызываемую функцию.

Пример того, почему компилятор не может определить внешнюю динамическую область действия функции, рассмотрим наш последний пример, если мы напишем что-то вроде этого:

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

Цепочка вызовов зависит от условий выполнения. Если это правда, то цепочка вызовов выглядит следующим образом:

dummy1 --> fun()

Если условие ложно:

dummy2 --> fun()

Внешняя область действия fun в обоих случаях - это вызывающий абонент плюс вызывающий абонент и т. Д. .

Просто упомяну, что язык C не допускает вложенных функций и динамической области видимости.

240 голосов
/ 24 мая 2010

Давайте попробуем самое короткое определение:

Lexical Scoping определяет способ разрешения имен переменных во вложенных функциях: внутренние функции содержат область действия родительских функций, даже если родительская функция возвратила .

Вот и все!

46 голосов
/ 06 ноября 2013
var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

Приведенный выше код вернет сообщение "Я просто местный". Не вернется «Я глобальный». Поскольку функция func () считает, где изначально было определено, что входит в область действия whatismyscope.

Он не будет беспокоиться от того, как он вызывается (глобальная область действия / даже из другой функции), поэтому значение глобальной области действия, которое я являюсь глобальным, не будет напечатано.

Это называется лексической областью видимости, где " функции выполняются с использованием цепочки областей действия, которая действовала, когда они были определены " - в соответствии с Руководством по определению JavaScript.

Лексический прицел - очень очень мощная концепция.

Надеюсь, это поможет ..:)

38 голосов
/ 26 июня 2009

Область действия определяет область, в которой доступны функции, переменные и тому подобное. Например, доступность переменной определяется в ее контексте, скажем, в функции, файле или объекте, в котором они определены. Мы обычно называем эти локальные переменные.

Лексическая часть означает, что вы можете извлечь область из чтения исходного кода.

Лексическая область видимости также называется статической областью действия.

Динамическая область действия определяет глобальные переменные, которые можно вызывать или ссылаться из любого места после их определения. Иногда их называют глобальными переменными, хотя глобальные переменные в большинстве языков программирования имеют лексическую область. Это означает, что из чтения кода можно получить информацию о том, что переменная доступна в этом контексте. Может быть, нужно следовать условию использования или включения, чтобы найти указание или определение, но код / ​​компилятор знает о переменной в этом месте.

Напротив, в динамическом определении объема вы сначала выполняете поиск в локальной функции, затем - в функции, которая вызвала локальную функцию, затем - в функции, которая вызвала эту функцию, и так далее, вверх по стеку вызовов. «Динамический» относится к изменению в том смысле, что стек вызовов может отличаться каждый раз, когда вызывается данная функция, и поэтому функция может попадать в разные переменные в зависимости от того, откуда она вызывается. (см. здесь )

Чтобы увидеть интересный пример динамической области действия, см. здесь .

Подробнее см. здесь и здесь .

Некоторые примеры в Delphi / Object Pascal

Delphi имеет лексическую область видимости.

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.

Ближайший Delphi к динамической области видимости - это пара функций RegisterClass () / GetClass (). О его использовании см. здесь .

Допустим, время, когда RegisterClass ([TmyClass]) вызывается для регистрации определенного класса, нельзя предсказать, прочитав код (он вызывается в методе нажатия кнопки, вызываемом пользователем), код, вызывающий GetClass ('TmyClass' ) получит результат или нет. Вызов RegisterClass () не обязательно должен быть в лексической области модуля, использующего GetClass ();

Другая возможность для динамической области видимости - это анонимные методы (замыкания) в Delphi 2009, поскольку они знают переменные своей вызывающей функции. Он не следует рекурсивному пути вызова и поэтому не является полностью динамическим.

36 голосов
/ 26 июня 2009

Лексическая (AKA static) область действия относится к определению области действия переменной на основе исключительно ее положения в текстовом корпусе кода. Переменная всегда относится к ее среде верхнего уровня. Хорошо понимать это в отношении к динамическому объему.

31 голосов
/ 27 августа 2014

Мне нравятся полнофункциональные, независимые от языка ответы от таких людей, как @Arak. Поскольку этот вопрос был помечен JavaScript , я хотел бы добавить некоторые заметки, характерные для этого языка.

В javascript наш выбор области действия:

  • как есть (без регулировки объема)
  • лексический var _this = this; function callback(){ console.log(_this); }
  • связанный callback.bind(this)

Стоит отметить, я думаю, что JavaScript на самом деле не имеет динамической области видимости . .bind корректирует ключевое слово this, и это близко, но технически не то же самое.

Вот пример, демонстрирующий оба подхода. Вы делаете это каждый раз, когда принимаете решение о том, как настроить обратные вызовы, чтобы это относилось к обещаниям, обработчикам событий и т. Д.

Лексический

Вот что вы можете назвать Lexical Scoping обратных вызовов в 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

Еще один способ охвата - использовать Function.prototype.bind:

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

Эти методы, насколько мне известно, поведенчески эквивалентны.

12 голосов
/ 03 июля 2015

IBM определяет его как:

Часть программы или сегмента, в которой объявление применяется. Идентификатор, объявленный в подпрограмме, известен в рутина и во всех вложенных процедур. Если вложенная подпрограмма объявляет элемент с тем же именем, внешний элемент недоступен в вложенная рутина.

Пример 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();

Пример 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();
11 голосов
/ 23 сентября 2011

Лексическая область видимости: переменные, объявленные вне функции, являются глобальными переменными и видны повсюду в программе JavaScript. Переменные, объявленные внутри функции, имеют область видимости функции и видны только коду, который появляется внутри этой функции.

4 голосов
/ 22 февраля 2015

Существует важная часть разговора о лексическом и динамическом определении области действия, которая отсутствует: простое объяснение времени жизни переменной области действия - или , когда переменная может быть доступна ,

Динамическое определение объема только очень слабо соответствует «глобальному» определению в том смысле, в котором мы традиционно думаем об этом (причина, по которой я привожу сравнение между ними, заключается в том, что оно уже было упомянуто - и я не особенно нравится объяснение связанной статьи); вероятно, лучше всего не проводить сравнение между глобальным и динамическим - хотя предположительно, согласно связанной статье, «... [это] полезно в качестве замены глобально изменяемых переменных».

Итак, на простом английском языке, в чем заключается важное различие между двумя механизмами определения объема?

Лексическая область видимости была очень хорошо определена в ответах выше: переменные с лексической областью доступны - или доступны - на локальном уровне функции, в которой они были определены.

Однако - поскольку это не является предметом OP - динамическое определение области применения не получило большого внимания, и внимание, которое он получил, означает, что ему, вероятно, нужно немного больше (это не критика других ответов, а скорее «о, этот ответ заставил нас пожелать, чтобы было немного больше»). Итак, вот еще немного:

Динамическая область видимости означает, что переменная доступна для более крупной программы в течение времени жизни вызова функции - или во время выполнения функции. На самом деле, Википедия отлично справляется с объяснением разницы между . Чтобы не запутывать это, вот текст, который описывает динамическую область видимости:

... [I] n динамическая область видимости (или динамическая область), если область имени переменной определенная функция, то ее область действия - это период времени, в течение которого функция выполняется: пока функция работает, переменная имя существует и связано с его переменной, но после функции возвращается, имя переменной не существует.

3 голосов
/ 15 сентября 2014

Лексическая область действия означает, что функция ищет переменные в контексте, где она была определена, а не в области непосредственно вокруг нее.

Посмотрите, как работает лексическая область видимости в Лиспе, если вы хотите больше подробностей. Выбранный ответ Кайла Кронина в Динамические и Лексические переменные в Common Lisp намного яснее, чем ответы здесь.

По совпадению я узнал об этом только в классе Lisp, и это применимо и к JS.

Я запустил этот код в консоли 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)
})();                     //)

выход:

5
10
5 
...