Что такое лексический охват?
Может кто-нибудь, пожалуйста, дайте мне краткое введение в лексику?
Ответы
Ответ 1
Я понимаю их на примерах.:)
Во-первых, Lexical Scope (также называемый Static Scope), в синтаксисе C-like:
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.
Первый называется static, потому что он может быть выведен во время компиляции, второй называется динамическим, поскольку внешняя область является динамической и зависит от цепного вызова функций.
Я обнаружил, что статический обзор легче для глаза. Большинство языков пошло так, в конце концов, даже Lisp (могут делать оба, правильно?). Динамическое масштабирование похоже на передачу ссылок на все переменные вызываемой функции.
Пример того, почему компилятор не может вывести внешнюю динамическую область функции, рассмотрим наш последний пример, если мы напишем что-то вроде этого:
if(/* some condition */)
dummy1();
else
dummy2();
Цепочка вызовов зависит от условия времени выполнения. Если это правда, цепочка вызовов выглядит так:
dummy1 --> fun()
Если условие ложно:
dummy2 --> fun()
Внешний объем fun
в обоих случаях - это вызывающий, плюс вызывающий абонент и т.д.
Просто отметим, что язык C не допускает вложенных функций и динамического охвата.
Ответ 2
Давайте попробуем кратчайшее определение:
Лексическое определение определяет, как имена переменных разрешаются в вложенных функциях: внутренние функции содержат область родительских функций, даже если родительская функция вернула.
Вот и все! Чтобы понять, что это значит, я написал подробное сообщение в блоге о области и лексическом охвате в JavaScript, который можно найти здесь. Возможно, это может послужить и кому-то другому.
Ответ 3
Лексическое (AKA статическое) определение относится к определению области переменной, основанной исключительно на ее позиции в текстовом корпусе кода. Переменная всегда относится к среде верхнего уровня. Хорошо понимать это в отношение к динамической области.
Ответ 4
Область определяет область, где доступны функции, переменные и т.д. Доступность переменной, например, определена в ее контексте, пусть говорят функция, файл или объект, они определены в. Обычно мы называем эти локальные переменные.
Лексическая часть означает, что вы можете получить объем от чтения исходного кода.
Лексическая область также известна как статическая область.
Динамическая область определяет глобальные переменные, которые можно вызывать или ссылаться из любого места после определения. Иногда они называются глобальными переменными, хотя глобальные переменные на большинстве языков программирования являются лексической областью. Это означает, что он может быть получен из чтения кода, доступного этой переменной в этом контексте. Возможно, нужно следовать за использованием или включать предложение, чтобы найти установку или определение, но код/компилятор знает о переменной в этом месте.
В динамическом режиме, напротив, сначала вы выполняете поиск в локальной функции, затем выполняете поиск в функции, называемой локальной функцией, затем выполняете поиск в функции, вызывающей эту функцию, и т.д. вверх по стеку вызовов. "Динамический" относится к изменению, поскольку стек вызовов может быть различным каждый раз, когда вызывается данная функция, и поэтому функция может поражать разные переменные в зависимости от того, откуда она вызывается. (см. здесь)
Чтобы увидеть интересный пример для динамической области, см. здесь.
Подробнее см. здесь и здесь.
Некоторые примеры в 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(). Для его использования см. here.
Скажем, что время RegisterClass ([TmyClass]) вызывается для регистрации определенного класса, не может быть предсказано путем чтения кода (он вызывается в методе нажатия кнопки, вызываемом пользователем), код, вызывающий GetClass ('TmyClass' ) получит результат или нет. Вызов RegisterClass() не должен быть в лексической области устройства с использованием GetClass();
Еще одна возможность для динамического охвата - анонимные методы (закрытия) в Delphi 2009, поскольку они знают переменные их вызывающей функции. Он не рекурсивно следит за вызывающим путем и поэтому не полностью динамичен.
Ответ 5
var scope = "I am global";
function whatismyscope(){
var scope = "I am just a local";
function func() {return scope;}
return func;
}
whatismyscope()()
Вышеприведенный код вернет "Я просто локальный". Он не вернется "Я глобальный". Поскольку функция func() подсчитывает, где была изначально определена, которая находится в рамках функции whatismyscope.
Он не будет беспокоиться о том, что он вызывается (глобальная область/изнутри другой функции), поэтому значение глобальной области видимости, которое я глобально, не будет напечатано.
Это называется лексическим охватом, где "функции выполняются с использованием цепочки областей действия, которая действовала, когда они были определены" - согласно Руководству по определению JavaScript.
Лексическая область - очень очень мощная концепция.
Надеюсь, это поможет..:)
Ответ 6
Мне нравятся полнофункциональные, языковые агностические ответы от таких людей, как @Arak. Поскольку этот вопрос был отмечен JavaScript, я хотел бы отметить некоторые примечания, очень специфичные для этого языка.
В javascript наши варианты выбора:
- as-is (без регулировки области)
- лексический
var _this = this; function callback(){ console.log(_this); }
- bound
callback.bind(this)
Стоит отметить, что JavaScript не имеет динамического охвата. .bind
настраивает ключевое слово this
, и это близко, но не технически то же самое.
Вот пример, демонстрирующий оба подхода. Вы делаете это каждый раз, когда принимаете решение о том, как обращаться к обратным вызовам области, поэтому это относится к promises, обработчикам событий и т.д.
Вот что вы могли бы назвать 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`
}
//...
Эти методы, насколько мне известно, поведенчески эквивалентны.
Ответ 7
Лексическое определение: переменные, объявленные вне функции, являются глобальными переменными и отображаются везде в программе JavaScript. Переменные, объявленные внутри функции, имеют область действия и видны только для кода, который появляется внутри этой функции.
Ответ 8
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();
Ответ 9
Существует важная часть беседы вокруг лексического и динамического охвата, которая отсутствует: простое объяснение времени жизни переменной с областью - или когда доступ к переменной можно получить.
Динамическое масштабирование только очень слабо соответствует "глобальному" охвату в том виде, в котором мы традиционно думаем об этом (причина, по которой я поднимаю сравнение между ними, состоит в том, что она уже была упомянутый - и мне не особенно нравится ссылка); вероятно, лучше всего не проводить сравнение между глобальными и динамическими, хотя, предположительно, согласно связанной статье, "... [это] полезно в качестве замены переменных с глобальным охватом".
Итак, на простом английском языке, какое важное различие между двумя механизмами обзора?
Лексическое определение области обзора было хорошо определено во всех вышеперечисленных ответах: доступные лексико-переменные переменные доступны или доступны на локальном уровне функции, в которой она была определена.
Однако, поскольку это не является фокусом ОП - динамическое исследование не привлекло большого внимания, и полученное им внимание означает, что ему, вероятно, нужно немного больше (это не критика других ответов, а скорее "О, этот ответ заставил нас желать, чтобы было немного больше" ). Итак, здесь немного больше:
Динамическое масштабирование означает, что переменная доступна для более крупной программы в течение времени жизни вызова функции - или, во время выполнения функции. Действительно, Википедия действительно хорошо справляется с объяснением разницы между ними. Чтобы не запутывать его, вот текст, который описывает динамическое масштабирование:
... [I] n динамическое масштабирование (или динамическая область), если область имен переменных является определенная функция, то ее область действия - это период времени, в течение которого функция выполняет: во время выполнения функции переменная имя существует и привязано к его переменной, но после функции возвращает, имя переменной не существует.
Ответ 10
Лексическая область означает, что функция ищет переменные в контексте, где она была определена, а не в области, непосредственно связанной с ней.
Посмотрите, как лексическая область работает в Lisp, если вы хотите получить более подробную информацию. Выбранный ответ Кайла Кронина в Динамические и лексические переменные в Common Lisp намного яснее ответов здесь.
Кстати, я узнал об этом только в классе Lisp, и это также применимо и в JS.
Я запустил этот код в консоли хром.
// 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
Ответ 11
"Лексическая область" в целом - это идея, что:
Идентификатор в определенном месте в программе всегда ссылается на одно и то же место переменной - где "всегда" означает "каждый раз, когда выполняется содержащее выражение"
Место переменной, к которому оно относится, может быть определено статическим рассмотрением контекста исходного кода, в котором появляется этот идентификатор, без необходимости рассматривать поток выполнения через программу в целом.
function init() {
var name = 'Foo'; // `name` is a local variable created by init
function displayName() { // `displayName()` is the inner function, a closure
alert(name); // use variable declared in the parent function
}
displayName();
}
init();
init()
создает локальную переменную name
и функцию displayName()
. displayName()
- внутренняя функция, которая определена внутри init()
и доступна только в теле init()
. displayName()
не имеет собственных локальных переменных. Однако, поскольку внутренние функции имеют доступ к переменным внешних функций, displayName()
может получить доступ к имени переменной, объявленному в родительской функции, init()
.
Обратите внимание, что alert()
внутри displayName()
успешно отображает значение переменной name, которое объявляется в его родительской функции. Это пример лексического охвата, который описывает, как парсер разрешает имена переменных, когда функции вложены. Слово "лексическое" относится к тому, что в лексическом охвате используется место, где переменная объявляется в исходном коде, чтобы определить, где эта переменная доступна. Вложенные функции имеют доступ к переменным, объявленным в их внешней области.
Ответ 12
Здесь другой подход к этому вопросу, который мы можем получить, сделав шаг назад и рассмотрев роль обзора в более широких рамках интерпретации (запуск программы). Другими словами, представьте, что вы строили интерпретатор (или компилятор) для языка и отвечали за вычисление вывода, учитывая программу и некоторый ввод в нее.
Интерпретация включает в себя отслеживание трех вещей:
1) Состояние, а именно, переменные и ссылки на ячейки памяти в куче и стеке.
2) Операции над этим состоянием - а именно, каждая строка кода в вашей программе
3) Окружающая среда, в которой выполняется данная операция, а именно проекция государства на операцию.
Интерпретатор запускается в первой строке кода в программе, вычисляет его среду, запускает строку в этой среде и фиксирует ее влияние на состояние программы. Затем он следует за потоком управления программой для выполнения следующей строки кода и повторяет процесс до завершения программы.
То, как вы вычисляете среду для любой операции, - это формальный набор правил, определяемый языком программирования. Термин "привязка" часто используется для описания отображения общего состояния программы на значение в среде. Обратите внимание, что под "общим состоянием" мы не имеем в виду глобальное состояние, а скорее сумму всех достижимых определений в любой момент выполнения)
Это основа, в которой определена проблема определения области. Теперь к следующей части наших опций.
- Как разработчик интерпретатора вы могли бы упростить свою задачу, сделав среду как можно ближе к состоянию программы. Соответственно, среда строки кода будет просто определяться средой предыдущей строки кода с эффектами применяемой к ней операции, независимо от того, была ли предыдущая строка назначением, вызовом функции, возвратом от функции, или структуру управления, такую как цикл while.
Это суть динамического охвата, в котором среда, в которой работает любой код, связана с состоянием программы, как определено контекстом ее выполнения.
- Или вы могли бы подумать о программисте, использующем ваш язык, и упростите его или ее задачу отслеживать значения, которые может принять переменная. Есть слишком много путей и слишком много сложностей, связанных с рассуждением об итогах в совокупности прошлого исполнения. Лексическое масштабирование помогает сделать это, ограничивая текущую среду частью состояния, определенной в текущем блоке, функции или другой единице области, и ее родительской (т.е. Блоке, охватывающем текущие часы, или функции, вызывающей настоящую функцию).
Другими словами, с помощью Lexical Scope среда, которую видит какой-либо код, привязывается к области, определенной явно на языке, например блоке или функции.
Ответ 13
Лексическая область в Javascript означает, что переменная, определенная вне функции, может быть доступна внутри другой функции, определенной после объявления переменной. Но противоположное неверно, переменные, определенные внутри функции, не будут доступны вне этой функции.
Эта концепция широко используется в закрытии в Javascript.
Скажем, у нас есть код ниже.
var x = 2;
var add = function() {
var y = 1;
return x + y;
};
Теперь, когда вы вызываете add() ->, это будет печатать 3.
Таким образом, функция add() выполняет доступ к глобальной переменной x, которая определена до добавления функции функции. Это вызвано из-за лексического охвата в javascript.