Область применения (информатика)
Эта статья нуждается в дополнительных цитатах для проверки . ( декабрь 2008 г. ) |
В компьютерном программировании областью ; ( привязки имени связывание имени с объектом, например переменной ) является часть программы , где привязка имени действительна то есть, где имя может использоваться для ссылки на объект. В других частях программы имя может относиться к другой сущности (оно может иметь другую привязку) или вообще ни к чему (оно может быть несвязанным). Область видимости помогает предотвратить конфликты имен , позволяя одному и тому же имени ссылаться на разные объекты — при условии, что имена имеют разные области действия. Область привязки имени также известна как видимость объекта, особенно в более старой или более технической литературе — это относится к объекту, на который ссылаются, а не к ссылающемуся имени.
Термин «область действия» также используется для обозначения набора всех привязок имен, которые действительны в пределах части программы или в данной точке программы, что правильнее называть контекстом или средой . [а]
Строго говоря [б] и на практике для большинства языков программирования «часть программы» относится к части исходного кода (области текста) и известна как лексическая область видимости . Однако в некоторых языках «часть программы» относится к части времени выполнения (период во время выполнения ) и называется динамической областью действия . Оба этих термина в некоторой степени вводят в заблуждение — они неправильно используют технические термины, как обсуждается в определении , — но само различие является точным и точным, и это соответствующие стандартные термины. Лексическая область видимости находится в центре внимания этой статьи, причем динамическая область видимости понимается в отличие от лексической области видимости.
В большинстве случаев разрешение имен, основанное на лексической области видимости, относительно просто в использовании и реализации, поскольку при использовании можно прочитать исходный код в обратном направлении, чтобы определить, к какому объекту относится имя, а при реализации можно поддерживать список имен и контексты при компиляции или интерпретации программы. Трудности возникают при маскировке имен , предварительных объявлениях и подъеме , тогда как гораздо более тонкие трудности возникают с нелокальными переменными , особенно в замыканиях .
Определение
[ редактировать ]Строгое определение (лексической) «области» имени ( идентификатора ) однозначно: лексическая область действия — это «часть исходного кода, в которой применяется привязка имени к сущности». Оно практически не изменилось по сравнению с определением 1960 года в спецификации АЛГОЛА 60 . Ниже приведены типичные языковые характеристики:
- АЛГОЛ 60 (1960) [1]
- Различают следующие виды величин: простые переменные, массивы, метки, переключатели и процедуры. Областью действия количества является набор операторов и выражений, в которых допустимо объявление идентификатора, связанного с этим количеством.
- С (2007) [2]
- Идентификатор может обозначать объект; функция; тег или член структуры, объединения или перечисления; имя typedef ; название этикетки; имя макроса; или параметр макроса. Один и тот же идентификатор может обозначать разные объекты в разных точках программы. [...] Для каждого отдельного объекта, который обозначает идентификатор, идентификатор виден (т. е. может использоваться) только в области текста программы, называемой ее областью действия.
- Иди (2013) [3]
- Объявление привязывает непустой идентификатор к константе, типу, переменной, функции, метке или пакету. [...] Область объявленного идентификатора — это объем исходного текста, в котором идентификатор обозначает указанную константу, тип, переменную, функцию, метку или пакет.
Чаще всего «область действия» означает, что данное имя может ссылаться на данную переменную (когда объявление имеет эффект), но может также применяться к другим объектам, таким как функции, типы, классы, метки , константы и перечисления.
Лексическая область видимости и динамическая область видимости
[ редактировать ]Фундаментальное различие в сфере охвата заключается в том, что означает «часть программы». В языках с лексической областью действия (также называемой статической областью видимости ) разрешение имен зависит от местоположения в исходном коде и лексического контекста (также называемого статическим контекстом ), который определяется тем, где определена именованная переменная или функция. Напротив, в языках с динамической областью разрешение имени зависит от состояния программы , когда встречается имя, которое определяется контекстом выполнения (также называемым контекстом времени выполнения , контекстом вызова или динамическим контекстом ). На практике в лексической области имя определяется путем поиска в локальном лексическом контексте, затем, если это не удается, путем поиска во внешнем лексическом контексте и так далее; тогда как в динамической области имя разрешается путем поиска в локальном контексте выполнения, затем, если это не удается, путем поиска во внешнем контексте выполнения и т. д., продвигаясь вверх по стеку вызовов. [4]
В большинстве современных языков для переменных и функций используется лексическая область видимости, хотя в некоторых языках, особенно в некоторых диалектах Lisp, некоторых «скриптовых» языках и некоторых языках шаблонов , используется динамическая область видимости . [с] Perl 5 предлагает как лексическую, так и динамическую область видимости. Даже в языках с лексической областью действия замыкания могут сбить с толку непосвященных. [ нужна ссылка ] поскольку они зависят от лексического контекста, в котором определено замыкание, а не от того, где оно вызывается.
Лексическое разрешение может быть определено во время компиляции и также известно как раннее связывание , в то время как динамическое разрешение обычно может быть определено только во время выполнения и, следовательно, известно как позднее связывание .
Связанные понятия
[ редактировать ]В объектно-ориентированном программировании динамическая диспетчеризация объекта выбирает метод во время выполнения, хотя фактическая привязка имени выполняется во время компиляции или во время выполнения, зависит от языка. Де-факто динамическая область видимости распространена в макроязыках , которые не выполняют разрешение имен напрямую, а вместо этого расширяются на месте.
Некоторые среды программирования, такие как AngularJS, используют термин «область действия» для обозначения чего-то совершенно иного, чем то, как он используется в этой статье. В этих средах область видимости — это просто объект используемого ими языка программирования ( JavaScript в случае AngularJS), который определенным образом используется платформой для эмуляции динамической области видимости в языке, который использует лексическую область видимости для своих переменных. Эти области AngularJS сами могут находиться в контексте или не в контексте (используя обычное значение этого термина) в любой заданной части программы, следуя обычным правилам области переменных языка, как и любой другой объект, и используя собственное наследование и правила включения . В контексте AngularJS иногда используется термин «$scope» (со знаком доллара), чтобы избежать путаницы, но руководства по стилю часто не одобряют использование знака доллара в именах переменных. [5]
Использовать
[ редактировать ]Область видимости — важный компонент разрешения имен . [д] что, в свою очередь, имеет фундаментальное значение для семантики языка . Разрешение имен (включая область действия) варьируется в зависимости от языка программирования, а внутри языка программирования зависит от типа объекта; правила области называются правилами области (или правилами области действия ). Вместе с пространствами имен правила области имеют решающее значение в модульном программировании , поэтому изменение в одной части программы не нарушает работу несвязанной части.
Обзор
[ редактировать ]При обсуждении области действия существуют три основных понятия: область действия, степень и контекст. В частности, «область действия» и «контекст» часто путают: область действия — это свойство привязки имени, а контекст — это свойство части программы, которая является либо частью исходного кода ( лексический контекст или статический контекст ), либо часть времени выполнения ( контекст выполнения, контекст времени выполнения, контекст вызова или динамический контекст ). Контекст выполнения состоит из лексического контекста (в текущей точке выполнения) плюс дополнительного состояния времени выполнения, такого как стек вызовов . [и] Строго говоря, во время выполнения программа входит и выходит из областей различных привязок имен, и в какой-то момент выполнения привязки имен находятся «в контексте» или «не в контексте», следовательно, привязки имен «входят в контекст» или «выходят из контекста». ", когда выполнение программы входит в область видимости или выходит из нее. [ф] Однако на практике использование гораздо свободнее.
Область видимости — это концепция уровня исходного кода и свойство привязок имен, в частности привязок имен переменных или функций — имена в исходном коде являются ссылками на объекты в программе — и является частью поведения компилятора или интерпретатора языка. . Таким образом, вопросы области действия аналогичны указателям , которые представляют собой тип ссылки, используемый в программах в более общем плане. Использование значения переменной, когда имя находится в контексте, но переменная не инициализирована, аналогично разыменованию (доступу к значению) дикого указателя , поскольку он не определен. Однако, поскольку переменные не уничтожаются до тех пор, пока не выйдут из контекста, аналога висячего указателя не существует.
Для таких сущностей, как переменные, областью действия является подмножество времени жизни (также известное как экстент ) — имя может относиться только к существующей переменной (возможно, с неопределенным значением), но существующие переменные не обязательно видимы: переменная может существовать, но быть недоступным (значение сохраняется, но не упоминается в данном контексте) или доступным, но не по заданному имени, и в этом случае оно не находится в контексте (программа находится «вне области действия имени»). В других случаях «время жизни» не имеет значения — метка (именованная позиция в исходном коде) имеет время жизни, идентичное программе (для статически скомпилированных языков), но может находиться в контексте или не находиться в данном месте программы, и то же самое для статические переменные — статическая глобальная переменная находится в контексте всей программы, тогда как статическая локальная переменная находится только в контексте внутри функции или другого локального контекста, но обе имеют время жизни в течение всего запуска программы.
Определение того, к какому объекту относится имя, называется разрешением имени или привязкой имени (особенно в объектно-ориентированном программировании ) и различается в зависимости от языка. Учитывая имя, язык (точнее, компилятор или интерпретатор) проверяет все объекты, находящиеся в контексте, на предмет совпадений; в случае неоднозначности (две сущности с одинаковым именем, например, глобальная и локальная переменная с одинаковым именем), для их различения используются правила разрешения имен. Чаще всего разрешение имен основано на правиле «внутренний контекст — внешний», например правиле Python LEGB (локальный, охватывающий, глобальный, встроенный): имена неявно разрешаются в самый узкий соответствующий контекст. В некоторых случаях разрешение имен может быть указано явно, например, с помощью global
и nonlocal
ключевые слова в Python; в других случаях правила по умолчанию не могут быть переопределены.
Когда два идентичных имени находятся в контексте одновременно и относятся к разным объектам, говорят, что маскирование имени происходит , когда имя с более высоким приоритетом (обычно самое внутреннее) «маскирует» имя с более низким приоритетом. На уровне переменных это называется затенением переменных . Из-за возможности логических ошибок из-за маскировки некоторые языки запрещают или препятствуют маскировке, вызывая ошибку или предупреждение во время компиляции или во время выполнения.
Различные языки программирования имеют разные правила области действия для разных типов объявлений и имен. Такие правила области действия оказывают большое влияние на семантику языка и, следовательно, на поведение и корректность программ. В таких языках, как C++ , доступ к несвязанной переменной не имеет четко определенной семантики и может привести к неопределенному поведению , аналогичному обращению к висячему указателю ; а объявления или имена, используемые вне их области действия, будут генерировать синтаксические ошибки .
Области действия часто связаны с другими языковыми конструкциями и определяются неявно, но многие языки также предлагают конструкции специально для управления областью действия.
Уровни охвата
[ редактировать ]Область применения может варьироваться от одного выражения до всей программы со многими возможными градациями между ними. Простейшим правилом области действия является глобальная область действия: все объекты видны во всей программе. Самым основным правилом модульной области видимости является двухуровневая область видимости: глобальная область действия в любом месте программы и локальная область действия внутри функции. Более сложное модульное программирование позволяет использовать отдельную область действия модуля, где имена видны внутри модуля (частно для модуля), но не видны за его пределами. Внутри функции некоторые языки, такие как C, позволяют ограничить область действия блока подмножеством функции; другие, особенно функциональные языки, допускают область действия выражения, ограничивая область действия одним выражением. Другие области действия включают область действия файла (особенно в C), которая ведет себя аналогично области действия модуля, и область действия блока вне функций (особенно в Perl).
Тонкая проблема заключается в том, когда именно начинается и заканчивается область действия. В некоторых языках, таких как C, область действия имени начинается с объявления имени, поэтому разные имена, объявленные внутри данного блока, могут иметь разные области действия. Это требует объявления функций перед использованием, хотя и не обязательно их определения, а также требует предварительного объявления в некоторых случаях, особенно для взаимной рекурсии. В других языках, таких как Python, область действия имени начинается в начале соответствующего блока, где объявлено имя (например, в начале функции), независимо от того, где оно определено, поэтому все имена в данном блоке имеют тот же объем. В JavaScript область действия имени, объявленная с помощью let
или const
начинается с объявления имени, а область действия имени, объявленная с помощью var
начинается в начале функции, где объявлено имя, что известно как подъем переменной . Поведение имен в контексте, имеющих неопределенное значение, различается: в Python использование неопределенных имен приводит к ошибке времени выполнения, тогда как в JavaScript неопределенные имена, объявленные с помощью var
можно использовать во всей функции, поскольку они неявно привязаны к значению undefined
.
Область выражения
[ редактировать ]Областью привязки имени является выражение , известное как область выражения . Область действия выражения доступна во многих языках, особенно в функциональных языках, которые предлагают функцию, называемую выражениями let, позволяющую ограничить областью действия объявления одно выражение. Это удобно, если, например, для расчета необходимо промежуточное значение. Например, в Standard ML , если f()
возвращает 12
, затем let val x = f() in x * x end
это выражение, которое оценивается как 144
, используя временную переменную с именем x
чтобы не звонить f()
дважды. Некоторые языки с областью действия блока аппроксимируют эту функциональность, предлагая синтаксис для включения блока в выражение; например, вышеупомянутое стандартное выражение ML можно записать на Perl как do { my $x = f(); $x * $x }
или в GNU C как ({ int x = f(); x * x; })
.
В Python вспомогательные переменные в выражениях-генераторах и списках (в Python 3) имеют область действия выражения.
В C имена переменных в прототипе функции имеют область действия выражения, известную в этом контексте как область протокола функции . Поскольку имена переменных в прототипе не упоминаются (они могут отличаться в фактическом определении) — это просто пустышки — они часто опускаются, хотя их можно использовать, например, для создания документации.
Область действия блока
[ редактировать ]Областью привязки имени является блок , который называется областью блока . Область действия блока доступна во многих, но не во всех языках программирования с блочной структурой. Это началось с АЛГОЛА 60 , где «[е] каждое объявление... действительно только для этого блока». [6] и сегодня он особенно связан с языками семейств Pascal и C. и традиций Чаще всего этот блок содержится внутри функции, что ограничивает область действия частью функции, но в некоторых случаях, например в Perl, блок может находиться не внутри функции.
unsigned int sum_of_squares(const unsigned int N) {
unsigned int ret = 0;
for (unsigned int n = 1; n <= N; n++) {
const unsigned int n_squared = n * n;
ret += n_squared;
}
return ret;
}
Типичным примером использования области действия блока является показанный здесь код C, где две переменные включены в цикл: переменная цикла n , которая инициализируется один раз и увеличивается на каждой итерации цикла, и вспомогательная переменная n_squared , которая инициализируется на каждой итерации. Цель состоит в том, чтобы избежать добавления в область функции переменных, которые относятся только к определенному блоку — например, это предотвращает ошибки, когда универсальной переменной цикла i случайно уже присвоено другое значение. В этом примере выражение n * n
обычно не присваивается вспомогательной переменной, а тело цикла просто пишется ret += n * n
но в более сложных примерах полезны вспомогательные переменные.
Блоки в основном используются для потока управления, например, в циклах if, while и for, и в этих случаях область действия блока означает, что область действия переменной зависит от структуры потока выполнения функции. Однако языки с областью действия блока обычно также допускают использование «голых» блоков, единственная цель которых — обеспечить детальный контроль области видимости переменных. Например, вспомогательная переменная может быть определена в блоке, затем использована (скажем, добавлена к переменной с областью действия функции) и отброшена, когда блок заканчивается, или цикл while может быть заключен в блок, который инициализирует переменные, используемые внутри цикла. это должно быть инициализировано только один раз.
Тонкость некоторых языков программирования, таких как Algol 68 и C (продемонстрированная в этом примере и стандартизированная начиная с C99 ), заключается в том, что переменные области действия блока могут быть объявлены не только внутри тела блока, но и внутри управляющего оператора, если любой. Это аналогично параметрам функции, которые объявляются в объявлении функции (перед запуском блока тела функции) и в области действия всего тела функции. В основном это используется в циклах for , которые имеют оператор инициализации, отдельный от условия цикла, в отличие от циклов while, и являются распространенной идиомой.
Область действия блока можно использовать для затенения. В этом примере внутри блока вспомогательная переменная также могла бы называться n , скрывая имя параметра, но это считается плохим стилем из-за возможности возникновения ошибок. Более того, некоторые потомки C, такие как Java и C#, несмотря на поддержку области действия блока (в том смысле, что локальную переменную можно заставить выйти из контекста до завершения функции), не позволяют одной локальной переменной скрывать другую. . В таких языках попытка объявления второго n приведет к синтаксической ошибке, и одну из n переменных придется переименовать.
Если блок используется для установки значения переменной, область действия блока требует, чтобы переменная была объявлена вне блока. Это усложняет использование условных операторов с одним присваиванием . Например, в Python, который не использует область видимости блока, можно инициализировать переменную как таковую:
if c:
a = "foo"
else:
a = ""
где a
доступен после if
заявление.
В Perl, который имеет область действия блока, вместо этого требуется объявление переменной перед блоком:
my $a;
if (c) {
$a = 'foo';
} else {
$a = '';
}
Вместо этого часто это переписывается с использованием множественного присваивания, инициализируя переменную значением по умолчанию. В Python (где это не обязательно) это будет:
a = ""
if c:
a = "foo"
в то время как в Perl это будет:
my $a = '';
if (c) {
$a = 'foo';
}
В случае присваивания одной переменной альтернативой является использование тернарного оператора , чтобы избежать блока, но это, как правило, невозможно для присвоения нескольких переменных, и его трудно читать для сложной логики.
Это более серьезная проблема в C, особенно при назначении строк, поскольку инициализация строки может автоматически выделять память, тогда как присвоение строки уже инициализированной переменной требует выделения памяти, копирования строки и проверки их успешности.
{
my $counter = 0;
sub increment_counter {
return ++$counter;
}
}
Некоторые языки позволяют применять концепцию области действия блока в различной степени вне функции. Например, во фрагменте Perl справа: $counter
— имя переменной с областью действия блока (из-за использования my
ключевое слово), в то время как increment_counter
— имя функции с глобальной областью действия. Каждый звонок в increment_counter
увеличит ценность $counter
на единицу и верните новое значение. Код за пределами этого блока может вызывать increment_counter
, но не может иным образом получить или изменить значение $counter
. Эта идиома позволяет определять замыкания в Perl.
Область действия функции
[ редактировать ]Когда область видимости переменных, объявленных внутри функции, не выходит за пределы этой функции, это называется областью действия функции . [7] Область действия функции доступна в большинстве языков программирования, которые предлагают способ создания локальной переменной в функции или подпрограмме : переменной, область действия которой заканчивается (выходит из контекста) при возврате функции. В большинстве случаев время жизни переменной равно продолжительности вызова функции — это автоматическая переменная , создаваемая при запуске функции (или объявлении переменной), уничтожаемая при возврате из функции — при этом область видимости переменной находится в пределах функция, хотя значение слова «внутри» зависит от того, является ли область видимости лексической или динамической. Однако некоторые языки, такие как C, также предусматривают статические локальные переменные , где время жизни переменной равно всему времени жизни программы, но переменная находится только в контексте, когда находится внутри функции. В случае статических локальных переменных переменная создается при инициализации программы и уничтожается только при ее завершении, как и статическая глобальная переменная , но находится только в контексте внутри функции, как автоматическая локальная переменная.
Важно отметить, что в лексической области видимости переменная с областью действия функции имеет область действия только в пределах лексического контекста функции: она выходит из контекста, когда внутри функции вызывается другая функция, и возвращается в контекст, когда функция возвращается — вызываемые функции не имеют доступа. к локальным переменным вызывающих функций, а локальные переменные находятся только в контексте тела функции, в которой они объявлены. Напротив, в динамической области область действия распространяется на контекст выполнения функции: локальные переменные остаются в контексте при вызове другой функции и выходят из контекста только тогда, когда определяющая функция завершается, и, таким образом, локальные переменные находятся в контексте функции. в котором они определены и все названные функции . В языках с лексической областью действия и вложенными функциями локальные переменные находятся в контексте вложенных функций, поскольку они находятся в одном и том же лексическом контексте, но не для других функций, которые не являются лексически вложенными. Локальная переменная объемлющей функции называется нелокальная переменная для вложенной функции. Область действия функции также применима к анонимным функциям .
def square(n):
return n * n
def sum_of_squares(n):
total = 0
i = 0
while i <= n:
total += square(i)
i += 1
return total
Например, во фрагменте кода Python справа определены две функции: square
и sum_of_squares
. square
вычисляет квадрат числа; sum_of_squares
вычисляет сумму всех квадратов до числа. (Например, square(4)
4 2 = 16
, и sum_of_squares(4)
0 2 + 1 2 + 2 2 + 3 2 + 4 2 = 30
.)
Каждая из этих функций имеет переменную с именем n , которая представляет аргумент функции. Эти две переменные n полностью разделены и не связаны, несмотря на то, что имеют одно и то же имя, поскольку они являются локальными переменными с лексической областью действия и областью действия функции: областью действия каждой из них является собственная, лексически отдельная функция, и, следовательно, они не перекрываются. Поэтому, sum_of_squares
могу позвонить square
собственного n без изменения . Сходным образом, sum_of_squares
имеет переменные с именами total и i ; эти переменные из-за их ограниченной области действия не будут мешать переменным с именем total или i , которые могут принадлежать любой другой функции. Другими словами, нет риска конфликта имен между этими именами и любыми несвязанными именами, даже если они идентичны.
Маскирования имени не происходит: в любой момент времени в контексте находится только одна переменная с именем n , поскольку области действия не перекрываются. Напротив, если бы аналогичный фрагмент был написан на языке с динамической областью видимости, n в вызывающей функции остался бы в контексте вызываемой функции (области видимости перекрывались бы) и был бы замаскирован («затенен») новым n. в вызываемой функции.
Область действия функции значительно сложнее, если функции являются объектами первого класса и могут быть созданы локально для функции, а затем возвращены. В этом случае любые переменные во вложенной функции, которые не являются локальными для нее (несвязанные переменные в определении функции, которые разрешаются в переменные во включающем контексте), создают замыкание , поскольку не только сама функция, но и ее контекст (переменных) ) должен быть возвращен, а затем потенциально вызван в другом контексте. Это требует значительно большей поддержки со стороны компилятора и может усложнить анализ программы.
Область действия файла
[ редактировать ]Областью привязки имени является файл, который называется областью файла . Область действия файла во многом специфична для C (и C++), где область действия переменных и функций, объявленных на верхнем уровне файла (а не внутри какой-либо функции), охватывает весь файл — или, скорее, для C, от объявления до конца. исходный файл, а точнее единица перевода (внутренняя перелинковка). Это можно рассматривать как форму области действия модуля, где модули идентифицируются с файлами, а в более современных языках заменяется явной областью действия модуля. Из-за наличия операторов включения, которые добавляют переменные и функции во внутренний контекст и сами могут вызывать дополнительные операторы включения, может быть сложно определить, что находится в контексте в теле файла.
В приведенном выше фрагменте кода C имя функции sum_of_squares
имеет глобальную область действия (в C — внешняя связь). Добавление static
к сигнатуре функции приведет к созданию области файла (внутренняя связь).
Область действия модуля
[ редактировать ]Областью привязки имени является модуль, известный как область действия модуля . Область действия модуля доступна в модульных языках программирования , где модули (которые могут охватывать различные файлы) являются базовой единицей сложной программы, поскольку они позволяют скрывать и раскрывать информацию ограниченного интерфейса. Область действия модуля была впервые использована в семействе языков Modula , и Python (на который повлияла Modula) является типичным современным примером.
В некоторых объектно-ориентированных языках программирования, в которых отсутствует прямая поддержка модулей, например в C++ до C++20, [8] вместо этого аналогичная структура обеспечивается иерархией классов, где классы являются базовой единицей программы, а класс может иметь частные методы. Это правильно понимается в контексте динамической отправки, а не разрешения имен и области видимости, хотя они часто играют аналогичную роль. В некоторых случаях доступны обе эти возможности, например, в Python, который имеет как модули, так и классы, а организация кода (в виде функции уровня модуля или традиционно частного метода) является выбором программиста.
Глобальная область действия
[ редактировать ]Областью действия привязки имени является вся программа, известная как глобальная область действия . Имена переменных с глобальной областью действия, называемые глобальными переменными , часто считаются плохой практикой, по крайней мере, в некоторых языках, из-за возможности конфликтов имен и непреднамеренного маскировки, а также плохой модульности, а область действия функции или область действия блока считаются предпочтительными. Однако глобальная область видимости обычно используется (в зависимости от языка) для различных других типов имен, таких как имена функций, имена классов и имена других типов данных . такие механизмы, как пространства имен В этих случаях во избежание коллизий используются .
Лексическая область видимости и динамическая область видимости
[ редактировать ]Использование локальных переменных — имен переменных с ограниченной областью действия, которые существуют только внутри определенной функции — помогает избежать риска конфликта имен между двумя переменными с одинаковыми именами. Однако есть два совершенно разных подхода к ответу на этот вопрос: что значит быть «внутри» функции?
В лексической области видимости (или лексической области видимости ; также называемой статической областью или статической областью видимости ), если областью действия имени переменной является определенная функция, то ее областью действия является текст программы определения функции: внутри этого текста существует имя переменной и привязано к значению переменной, но вне этого текста имя переменной не существует. Напротив, в динамической области (или динамической области видимости ), если областью имени переменной является определенная функция, то ее областью действия является период времени, в течение которого функция выполняется: пока функция выполняется, имя переменной существует и привязано к ее значению, но после возврата из функции имя переменной не существует. Это означает, что если функция f
вызывает отдельно определенную функцию g
, то в лексической области видимости функция g
не имеет доступа к f
локальные переменные (при условии, что текст g
находится не внутри текста f
), находясь в динамической области, функция g
имеет доступ к f
локальные переменные (поскольку g
вызывается во время вызова f
).
$ # bash language
$ x=1
$ function g() { echo $x ; x=2 ; }
$ function f() { local x=3 ; g ; }
$ f # does this print 1, or 3?
3
$ echo $x # does this print 1, or 2?
1
Рассмотрим, например, программу справа. Первая линия, x=1
, создает глобальную переменную x
и инициализирует его 1
. Вторая линия, function g() { echo $x ; x=2 ; }
, определяет функцию g
который печатает («эхо») текущее значение x
, а затем устанавливает x
к 2
(перезаписывая предыдущее значение). Третья линия, function f() { local x=3 ; g ; }
определяет функцию f
который создает локальную переменную x
(скрывая глобальную переменную с таким же именем) и инициализирует ее значением 3
, а затем звонит g
. Четвертая линия, f
, звонки f
. Пятая линия, echo $x
, распечатывает текущее значение x
.
Итак, что именно печатает эта программа? Это зависит от правил области действия. Если язык этой программы использует лексическую область видимости, то g
печатает и изменяет глобальную переменную x
(потому что g
определяется снаружи f
), поэтому программа печатает 1
а потом 2
. Напротив, если этот язык использует динамическую область видимости, тогда g
печатает и изменяет f
локальная переменная x
(потому что g
вызывается изнутри f
), поэтому программа печатает 3
а потом 1
. (Так получилось, что язык программы — Bash , который использует динамическую область видимости; поэтому программа печатает 3
а потом 1
. Если бы тот же код был запущен с помощью ksh93 , использующего лексическую область видимости, результаты были бы другими.)
Лексическая область видимости
[ редактировать ]В лексической области имя всегда относится к своему лексическому контексту. Это свойство текста программы, и делает его независимым от стека вызовов реализация языка времени выполнения. Поскольку для такого сопоставления требуется только анализ статического текста программы, этот тип области видимости также называется статической областью действия . Лексическая область видимости является стандартной во всех языках на основе ALGOL , таких как Pascal , Modula-2 и Ada, а также в современных функциональных языках, таких как ML и Haskell . Он также используется в языке C и его синтаксических и семантических родственниках, хотя и с различными ограничениями. Статическая область видимости позволяет программисту рассматривать ссылки на объекты, такие как параметры, переменные, константы, типы, функции и т. д., как простые замены имен. Это значительно упрощает создание модульного кода и его анализ, поскольку локальную структуру именования можно понять изолированно. Напротив, динамическая область видимости заставляет программиста предвидеть все возможные контексты выполнения, в которых может быть вызван код модуля.
program A;
var I:integer;
K:char;
procedure B;
var K:real;
L:integer;
procedure C;
var M:real;
begin
(*scope A+B+C*)
end;
(*scope A+B*)
end;
(*scope A*)
end.
Например, Паскаль имеет лексическую область видимости. Рассмотрим фрагмент программы на языке Паскаль справа. Переменная I
виден во всех точках, поскольку он никогда не скрыт другой переменной с тем же именем. char
переменная K
виден только в основной программе, поскольку скрыт real
переменная K
видимый в процедуре B
и C
только. Переменная L
также видно только в процедуре B
и C
но он не скрывает какую-либо другую переменную. Переменная M
виден только в процедуре C
и поэтому недоступен ни из процедуры B
или основная программа. Также процедура C
виден только в процедуре B
и поэтому его нельзя вызвать из основной программы.
могла быть другая процедура C
объявлено в программе вне процедуры B
. Место в программе, где " C
" упоминается, а затем определяет, какая из двух процедур названа C
таким образом, он представляет собой полную аналогию с областью действия переменных.
Корректная реализация лексической области видимости в языках с первоклассными вложенными функциями нетривиальна, поскольку требует, чтобы каждое значение функции несло с собой запись значений переменных, от которых оно зависит (пара функции и этого контекста называется закрытие ) . В зависимости от реализации и архитектуры компьютера переменных поиск может стать немного неэффективным. [ нужна ссылка ] когда очень глубоко лексически вложенные функции, хотя существуют хорошо известные методы, позволяющие смягчить это. используются [9] [10] Кроме того, для вложенных функций, которые ссылаются только на свои собственные аргументы и (непосредственно) локальные переменные, все относительные местоположения могут быть известны во время компиляции . Таким образом, при использовании этого типа вложенной функции не возникает никаких накладных расходов. То же самое относится к отдельным частям программы, где вложенные функции не используются, и, естественно, к программам, написанным на языке, где вложенные функции недоступны (например, в языке C).
История
[ редактировать ]Лексическая область видимости впервые была использована в начале 1960-х годов для императивного языка АЛГОЛ 60 и с тех пор была использована в большинстве других императивных языков. [4]
Такие языки, как Паскаль и C, всегда имели лексическую область применения, поскольку оба они находятся под влиянием идей, вошедших в АЛГОЛ 60 и АЛГОЛ 68 (хотя C не включал лексически вложенные функции ).
Perl — это язык с динамической областью действия, к которой впоследствии добавилась статическая область видимости.
Оригинальный интерпретатор Лиспа (1960 г.) использовал динамическую область видимости. Глубокая привязка , которая приближается к статической (лексической) области видимости, была введена примерно в 1962 году в LISP 1.5 (через устройство Funarg , разработанное Стивом Расселом , работавшим под руководством Джона Маккарти ).
Все ранние Lisp использовали динамическую область видимости, когда были основаны на интерпретаторах. В 1982 году Гай Л. Стил-младший и группа Common LISP публикуют обзор Common LISP . [11] краткий обзор истории и различных реализаций Lisp до того момента, а также обзор функций, которыми должна обладать реализация Common Lisp . На странице 102 читаем:
Большинство реализаций LISP внутренне противоречивы: по умолчанию интерпретатор и компилятор могут назначать разную семантику правильным программам; это связано прежде всего с тем фактом, что интерпретатор предполагает, что все переменные имеют динамическую область видимости, в то время как компилятор предполагает, что все переменные являются локальными, если он не вынужден предположить иное. Это сделано ради удобства и эффективности, но может привести к очень тонким ошибкам. Определение Common LISP позволяет избежать таких аномалий, явно требуя от интерпретатора и компилятора налагать идентичную семантику на правильные программы.
Таким образом, реализации Common LISP должны были иметь лексическую область видимости . Опять же, из обзора Common LISP :
Кроме того, Common LISP предлагает следующие возможности (большинство из которых заимствованы из MacLisp, InterLisp или Lisp Machines Lisp): (...) Переменные с полной лексической областью видимости. Так называемая « проблема FUNARG ». [12] [13] полностью решена как в нисходящем, так и в восходящем случае.
К тому же году, когда был опубликован «Обзор Common LISP» (1982), были опубликованы первоначальные проекты (также Гаем Л. Стилом-младшим) скомпилированного Lisp с лексической областью действия, называемого Scheme , и предпринимались попытки реализации компилятора. В то время многие опасались, что лексическая область видимости в Лиспе будет неэффективной в реализации. В «Истории Т » [14] Олин Шиверс пишет:
Все серьезные Lisp-ы, использовавшиеся в то время в производстве, имели динамическую область видимости. Никто, кто не читал внимательно «Кролика», [15] диссертация (написанная Гаем Льюисом Стилом-младшим в 1978 году) полагала, что лексический объем будет иметь успех; даже те немногие, кто прочитал это, немного поверили, что это сработает в серьезном производственном использовании.
Термин «лексическая область видимости» появился как минимум в 1967 году. [16] в то время как термин «лексическая область видимости» появился как минимум в 1970 году, когда он использовался в Project MAC для описания правил области действия диалекта Lisp MDL (тогда известного как «Muddle»). [17]
Динамическая область видимости
[ редактировать ]В динамической области имя относится к контексту выполнения. С технической точки зрения это означает, что каждое имя имеет глобальный стек привязок. Введение локальной переменной с именем x
помещает привязку в глобальный x
стек (который мог быть пустым), который извлекается, когда поток управления покидает область действия. Оценка x
в любом контексте всегда дает верхнюю привязку. Обратите внимание, что это невозможно сделать во время компиляции, поскольку стек привязки существует только во время выполнения , поэтому этот тип области называется динамической областью.
Динамическая область видимости редко встречается в современных языках. [4]
Обычно определенные блоки определяются для создания привязок, время жизни которых равно времени выполнения блока; это добавляет некоторые функции статической области к процессу динамической области. Однако, поскольку раздел кода может быть вызван из множества разных мест и ситуаций, с самого начала может быть сложно определить, какие привязки будут применяться при использовании переменной (или существует ли она вообще). Это может быть полезно; применение принципа наименьшего знания предполагает, что код не зависит от причин (или обстоятельств) значения переменной, а просто использует значение в соответствии с определением переменной. Такая узкая интерпретация общих данных может обеспечить очень гибкую систему для адаптации поведения функции к текущему состоянию (или политике) системы. Однако это преимущество основано на тщательном документировании всех переменных, используемых таким образом, а также на тщательном исключении предположений о поведении переменных и не предоставляет никакого механизма для обнаружения взаимодействия между различными частями программы. Некоторые языки, например Perl и Common Lisp позволяют программисту выбирать статическую или динамическую область действия при определении или переопределении переменной. Примеры языков, использующих динамическую область видимости, включают Logo , Emacs Lisp , LaTeX и языки оболочки bash , Dash и PowerShell .
Динамическую область видимости довольно легко реализовать. Чтобы найти значение имени, программа может пройти через стек времени выполнения, проверяя каждую запись активации (кадр стека каждой функции) на предмет значения имени. На практике это становится более эффективным за счет использования списка ассоциаций , который представляет собой стек пар имя/значение. Пары помещаются в этот стек при каждом объявлении и извлекаются всякий раз, когда переменные выходят из контекста. [18] Мелкая привязка — это альтернативная стратегия, которая значительно быстрее и использует центральную справочную таблицу , которая связывает каждое имя с собственным набором значений. Это позволяет избежать линейного поиска во время выполнения для поиска определенного имени, но следует позаботиться о том, чтобы правильно поддерживать эту таблицу. [18] последним пришел — первым вышел» ( LIFO Обратите внимание, что обе эти стратегии предполагают порядок привязки по принципу « ); на практике все привязки упорядочены именно так.
Еще более простая реализация — представление динамических переменных с помощью простых глобальных переменных. Локальная привязка выполняется путем сохранения исходного значения в анонимном месте стека, невидимом для программы. Когда эта область привязки завершается, исходное значение восстанавливается из этого местоположения. Фактически, именно таким образом возникла динамическая область видимости. Ранние реализации Lisp использовали эту очевидную стратегию реализации локальных переменных, и эта практика сохранилась в некоторых диалектах, которые используются до сих пор, таких как GNU Emacs Lisp. Лексическая область видимости была введена в Лисп позже. Это эквивалентно описанной выше схеме поверхностной привязки, за исключением того, что центральная справочная таблица — это просто контекст привязки глобальной переменной, в котором текущее значение переменной является ее глобальным значением. Поддерживать глобальные переменные несложно. Например, объект-символ может иметь выделенный слот для своего глобального значения.
Динамическая область видимости обеспечивает отличную абстракцию для локального хранилища потоков , но если она используется таким образом, она не может основываться на сохранении и восстановлении глобальной переменной. Возможная стратегия реализации заключается в том, чтобы каждая переменная имела локальный ключ потока. При доступе к переменной локальный ключ потока используется для доступа к ячейке локальной памяти потока (с помощью кода, сгенерированного компилятором, который знает, какие переменные являются динамическими, а какие лексическими). Если локальный ключ потока не существует для вызывающего потока, то используется глобальное местоположение. Когда переменная локально привязана, предыдущее значение сохраняется в скрытом месте стека. Локальное хранилище потока создается под ключом переменной, и новое значение сохраняется там. Дальнейшие вложенные переопределения переменной внутри этого потока просто сохраняют и восстанавливают это локальное местоположение потока. Когда исходный, самый внешний контекст переопределения завершается, локальный ключ потока удаляется, снова предоставляя глобальную версию переменной этому потоку.
При ссылочной прозрачности динамическая область видимости ограничивается только стеком аргументов текущей функции и совпадает с лексической областью видимости.
Расширение макросов
[ редактировать ]В современных языках расширение макросов в препроцессоре является ключевым примером фактической динамической области видимости. Макроязык сам по себе только преобразует исходный код, не разрешая имена, но, поскольку расширение выполняется на месте, когда имена в расширенном тексте затем разрешаются (особенно свободные переменные), они разрешаются в зависимости от того, где они развернуты (в общих чертах). «вызванный»), как если бы происходила динамическая область видимости.
Препроцессор C , используемый для расширения макроса , де-факто имеет динамическую область действия, поскольку он не выполняет разрешение имен сам по себе и не зависит от того, где определен макрос. Например, макрос:
#define ADD_A(x) x + a
расширится, чтобы добавить a
к переданной переменной, причем это имя только позже разрешается компилятором в зависимости от того, где находится макрос ADD_A
«называется» (собственно, развернуто). На самом деле препроцессор C выполняет только лексический анализ , расширяя макрос на этапе токенизации, но не анализируя синтаксическое дерево и не выполняя разрешение имен.
Например, в следующем коде имя a
в макросе разрешается (после расширения) в локальную переменную на сайте расширения:
#define ADD_A(x) x + a
void add_one(int *x) {
const int a = 1;
*x = ADD_A(*x);
}
void add_two(int *x) {
const int a = 2;
*x = ADD_A(*x);
}
Полноценные имена
[ редактировать ]Как мы видели, одна из ключевых причин использования области видимости заключается в том, что она помогает предотвратить конфликты имен, позволяя одинаковым именам ссылаться на разные объекты, с тем ограничением, что имена должны иметь разные области видимости. Иногда это ограничение неудобно; когда в программе должно быть доступно много разных вещей, обычно всем им нужны имена с глобальной областью действия, поэтому требуются разные методы, чтобы избежать конфликтов имен.
Чтобы решить эту проблему, многие языки предлагают механизмы организации глобальных имен. Детали этих механизмов и используемые термины зависят от языка; но общая идея состоит в том, что группе имен может быть присвоено имя — префикс — и, при необходимости, к объекту можно обращаться по квалифицированному имени, состоящему из имени и префикса. Обычно такие имена в некотором смысле имеют два набора областей: область (обычно глобальную область), в которой видно полное имя, и одну или несколько более узких областей, в которых неквалифицированное имя (без префикса) отображается как хорошо. И обычно эти группы сами могут быть организованы в группы; то есть они могут быть вложенными .
Хотя многие языки поддерживают эту концепцию, детали сильно различаются. В некоторых языках есть механизмы, такие как пространства имен в C++ и C# , которые служат почти исключительно для организации глобальных имен в группы. В других языках есть механизмы, такие как пакеты в Ada и структуры в Standard ML , которые сочетают это с дополнительной целью сделать некоторые имена видимыми только для других членов их группы. И объектно-ориентированные языки часто позволяют классам или одноэлементным объектам выполнять эту задачу (независимо от того, есть ли у них также механизм, для которого это является основной целью). Более того, языки часто объединяют эти подходы; например, пакеты Perl во многом аналогичны пространствам имен C++, но могут выступать в качестве классов объектно-ориентированного программирования; а Java организует свои переменные и функции в классы, а затем организует эти классы в пакеты, подобные Ada.
По языку
[ редактировать ]Этот раздел нуждается в расширении . Вы можете помочь, добавив к нему . ( апрель 2013 г. ) |
Ниже приведены правила области применения для репрезентативных языков.
С
[ редактировать ]В C область видимости традиционно называется связью или видимостью , особенно для переменных. C — это язык с лексической областью действия, имеющий глобальную область действия (известную как внешняя связь ), форму области действия модуля или области действия файла (известную как внутренняя связь ) и локальную область действия (внутри функции); внутри области функций могут быть вложены через область действия блока. Однако стандарт C не поддерживает вложенные функции.
Время жизни и видимость переменной определяются ее классом хранения . В языке C существует три типа времени жизни: статическое (выполнение программы), автоматическое (выполнение блока, выделенного в стеке) и ручное (выделение в куче). Для переменных поддерживаются только статические и автоматические значения, которые обрабатываются компилятором, тогда как выделенная вручную память должна отслеживаться вручную для разных переменных. В C существует три уровня видимости: внешняя связь (глобальная), внутренняя связь (примерно файловая) и область действия блока (включающая функции); Области блоков могут быть вложенными, а с помощью включений возможны различные уровни внутренней связи. Внутренняя связь в C — это видимость на уровне единицы перевода , а именно исходного файла после обработки препроцессором C , в частности, включая все соответствующие включения.
Программы на языке C компилируются как отдельные объектные файлы , которые затем компонуются в исполняемый файл или библиотеку с помощью компоновщика . Таким образом, разрешение имен разделено между компилятором, который разрешает имена внутри единицы перевода (более широко, «единица компиляции», но это, по сути, другая концепция), и компоновщиком, который разрешает имена в единицах перевода; см. ссылку для дальнейшего обсуждения.
В C переменные с областью действия блока входят в контекст, когда они объявлены (не в верхней части блока), выходят из контекста, если какая-либо (невложенная) функция вызывается внутри блока, возвращаются в контекст, когда функция возвращается, и выйти из контекста в конце блока. В случае автоматических локальных переменных они также выделяются при объявлении и освобождаются в конце блока, тогда как для статических локальных переменных они выделяются при инициализации программы и освобождаются при ее завершении.
Следующая программа демонстрирует переменную с областью действия блока, которая входит в контекст в середине блока, а затем выходит из контекста (и фактически освобождается), когда блок заканчивается:
#include <stdio.h>
int main(void) {
char x = 'm';
printf("%c\n", x);
{
printf("%c\n", x);
char x = 'b';
printf("%c\n", x);
}
printf("%c\n", x);
}
Программа выводит:
m m b m
В C есть и другие уровни области видимости. [19] Имена переменных, используемые в прототипе функции, имеют видимость прототипа функции и контекст выхода в конце прототипа функции. Поскольку имя не используется, это бесполезно для компиляции, но может быть полезно для документации. Имена меток для оператора GOTO имеют область действия функции.
С++
[ редактировать ]Все переменные, которые мы собираемся использовать в программе, должны быть объявлены с указанием их типа в более ранней версии. точку в коде, как мы делали в предыдущем коде в начале тела функции main, когда мы объявил, что a, b и result имеют тип int. Переменная может иметь глобальную или локальную область действия. Глобальная переменная — это переменная, объявленная в основном теле исходный код вне всех функций, а локальная переменная объявлена внутри тела функции или блока.
Современные версии допускают вложенную лексическую область видимости.
Быстрый
[ редактировать ]Swift имеет аналогичное правило для областей действия C++, но содержит другие модификаторы доступа.
Модификатор | Непосредственный объем | Файл | Содержащий модуль/пакет | Остальной мир |
---|---|---|---|---|
открыть | Да | Да | Да | Да, позволяет подкласс |
общественный | Да | Да | Да | Да, запрещает подкласс |
внутренний | Да | Да | Да | Нет |
личный файл | Да | Да | Нет | Нет |
частный | Да | Нет | Нет | Нет |
Идти
[ редактировать ]Go лексически ограничен блоками. [3]
Ява
[ редактировать ]Java имеет лексическую область видимости.
Класс Java имеет несколько типов переменных: [20]
- Локальные переменные
- определяются внутри метода или конкретного блока. Эти переменные являются локальными для того места, где они были определены, и для более низких уровней. Например, цикл внутри метода может использовать локальные переменные этого метода, но не наоборот. Переменные цикла (локальные для этого цикла) уничтожаются, как только цикл завершается.
- Переменные-члены
- также называют Полями переменные, объявленные внутри класса, вне любого метода. По умолчанию эти переменные доступны для всех методов внутри этого класса, а также для всех классов в пакете.
- Параметры
- являются переменными в объявлениях методов.
В общем, набор скобок определяет конкретную область действия, но переменные на верхнем уровне внутри класса могут различаться по своему поведению в зависимости от ключевых слов-модификаторов, используемых в их определении. В следующей таблице показан доступ к членам, разрешенный каждым модификатором. [21]
Модификатор | Сорт | Упаковка | Подкласс | Мир |
---|---|---|---|---|
общественный | Да | Да | Да | Да |
защищенный | Да | Да | Да | Нет |
(без модификатора) | Да | Да | Нет | Нет |
частный | Да | Нет | Нет | Нет |
JavaScript
[ редактировать ]В JavaScript есть простые правила области видимости . [22] но правила инициализации переменных и разрешения имен могут вызывать проблемы, а широкое использование замыканий для обратных вызовов означает, что лексический контекст функции при ее определении (который используется для разрешения имен) может сильно отличаться от лексического контекста при ее вызове (что не имеет значения для разрешения имен). Объекты JavaScript имеют разрешение имен свойств, но это отдельная тема.
JavaScript имеет лексическую область видимости [23] вложен на уровне функции, при этом глобальный контекст является самым внешним контекстом. Эта область используется как для переменных, так и для функций (то есть объявлений функций, а не переменных типа функции ). [24] Заблокируйте область действия с помощью let
и const
ключевые слова являются стандартными, начиная с ECMAScript 6. Область действия блока можно создать, обернув весь блок в функцию и затем выполнив ее; это известно как шаблон выражения функции с немедленным вызовом (IIFE).
Хотя область действия JavaScript проста — лексическая, на уровне функций — связанные с ней правила инициализации и разрешения имен вызывают путаницу. Во-первых, присвоение имени вне области действия по умолчанию приводит к созданию новой глобальной переменной, а не локальной. Во-вторых, чтобы создать новую локальную переменную, необходимо использовать var
ключевое слово; затем переменная создается в верхней части функции со значением undefined
и переменной присваивается ее значение при достижении выражения присваивания:
- Переменной с инициализатором присваивается значение ее AssignmentExpression при выполнении VariableStatement , а не при создании переменной. [25]
Это известно как переменный подъем. [26] — объявление, но не инициализация, поднимается в начало функции. В-третьих, доступ к переменным до инициализации дает undefined
, а не синтаксическая ошибка. В-четвертых, для объявлений функций и объявление, и инициализация поднимаются в начало функции, в отличие от инициализации переменных. Например, следующий код создает диалоговое окно с выводом неопределенный , поскольку объявление локальной переменной поднимается, затеняя глобальную переменную, но инициализация - нет, поэтому переменная при использовании не определена:
a = 1;
function f() {
alert(a);
var a = 2;
}
f();
Кроме того, поскольку функции являются объектами первого класса в JavaScript и часто назначаются в качестве обратных вызовов или возвращаются из функций, при выполнении функции разрешение имени зависит от того, где оно было первоначально определено (лексический контекст определения), а не от лексического контекста определения. контекст или контекст выполнения, в котором он вызывается. Вложенные области действия конкретной функции (от самой глобальной до самой локальной) в JavaScript, в частности замыкания, используемого в качестве обратного вызова, иногда называются цепочкой областей действия по аналогии с цепочкой прототипов объекта.
Замыкания можно создавать в JavaScript с помощью вложенных функций, поскольку функции являются объектами первого класса. [27] Возврат вложенной функции из включающей функции включает локальные переменные включающей функции в качестве (нелокального) лексического контекста возвращаемой функции, что приводит к замыканию. Например:
function newCounter() {
// return a counter that is incremented on call (starting at 0)
// and which returns its new value
var a = 0;
var b = function() { a++; return a; };
return b;
}
c = newCounter();
alert(c() + ' ' + c()); // outputs "1 2"
Замыкания часто используются в JavaScript, поскольку используются для обратных вызовов. Действительно, любое перехват функции в локальном контексте в качестве обратного вызова или возврат ее из функции создает замыкание, если в теле функции есть какие-либо несвязанные переменные (при этом контекст замыкания основан на вложенных областях текущего лексического контекста). или «цепочка областей действия»); это может быть случайно. При создании обратного вызова на основе параметров параметры должны храниться в замыкании, иначе будет случайно создано замыкание, ссылающееся на переменные во включающем контексте, который может измениться. [28]
Разрешение имен свойств объектов JavaScript основано на наследовании в дереве прототипов — путь к корню в дереве называется цепочкой прототипов — и отделено от разрешения имен переменных и функций.
Лисп
[ редактировать ]Диалекты Лиспа имеют различные правила области видимости.
Оригинальный Лисп использовал динамическую область видимости; именно Scheme , вдохновленная ALGOL , ввела статическую (лексическую) область видимости в семействе Lisp.
Maclisp по умолчанию использовал динамическую область видимости в интерпретаторе и лексическую область видимости по умолчанию в скомпилированном коде, хотя скомпилированный код мог получить доступ к динамическим привязкам с помощью SPECIAL
объявления для определенных переменных. [29] Однако Maclisp рассматривал лексическое связывание скорее как оптимизацию, чем можно было бы ожидать от современных языков, и в нем не было функции закрытия , которую можно было бы ожидать от лексической области видимости в современных Lisps. Отдельная операция, *FUNCTION
, можно было несколько неуклюже обойти некоторые из этих проблем. [30]
Common Lisp перенял лексическую область видимости от Scheme . [31] как и Clojure .
ISLISP имеет лексическую область видимости для обычных переменных. Он также имеет динамические переменные, но они во всех случаях явно помечены; они должны быть определены defdynamic
особой формы, связанный dynamic-let
специальной форме и доступ к которому осуществляется явным dynamic
особая форма. [32]
Некоторые другие диалекты Lisp, такие как Emacs Lisp , по-прежнему используют динамическую область видимости по умолчанию. Emacs Lisp теперь имеет лексическую область видимости, доступную для каждого буфера. [33]
Питон
[ редактировать ]Для переменных Python имеет область действия функции, область действия модуля и глобальную область видимости. Имена входят в контекст в начале области (функция, модуль или глобальная область) и выходят из контекста, когда вызывается невложенная функция или заканчивается область. Если имя используется до инициализации переменной, это вызывает исключение во время выполнения. Если к переменной просто обращаются (а не присваивают ее), разрешение имен соответствует правилу LEGB (локальное, охватывающее, глобальное, встроенное), которое разрешает имена в самом узком соответствующем контексте. Однако если переменной присвоено значение, по умолчанию объявляется переменная, область действия которой начинается в начале уровня (функции, модуля или глобального), а не в момент назначения. Оба эти правила могут быть отменены с помощью global
или nonlocal
(в Python 3) объявление перед использованием, которое позволяет получить доступ к глобальным переменным, даже если существует маскирующая нелокальная переменная, и присваивать значения глобальным или нелокальным переменным.
В качестве простого примера: функция преобразует переменную в глобальную область видимости:
>>> def f():
... print(x)
...
>>> x = "global"
>>> f()
global
Обратите внимание, что x
определяется раньше f
вызывается, поэтому ошибка не возникает, даже если он определен после ссылки на него в определении f
. Лексически это прямая ссылка , которая разрешена в Python.
Здесь присваивание создает новую локальную переменную, которая не меняет значение глобальной переменной:
>>> def f():
... x = "f"
... print(x)
...
>>> x = "global"
>>> print(x)
global
>>> f()
f
>>> print(x)
global
Присвоение переменной внутри функции приводит к тому, что она объявляется локально по отношению к функции, следовательно, ее областью действия является вся функция, и, таким образом, ее использование до этого присвоения вызывает ошибку. Это отличается от C, где область действия локальной переменной начинается с ее объявления. Этот код вызывает ошибку:
>>> def f():
... print(x)
... x = "f"
...
>>> x = "global"
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment
Правила разрешения имен по умолчанию можно переопределить с помощью global
или nonlocal
(в Python 3) ключевые слова. В приведенном ниже коде global x
декларация в g
означает, что x
преобразуется в глобальную переменную. Таким образом, к нему можно получить доступ (так как он уже определен), и присваивание присваивается глобальной переменной, а не объявляется новая локальная переменная. Обратите внимание, что нет global
декларация необходима в f
— поскольку он не присваивается переменной, по умолчанию он разрешается в глобальную переменную.
>>> def f():
... print(x)
...
>>> def g():
... global x
... print(x)
... x = "g"
...
>>> x = "global"
>>> f()
global
>>> g()
global
>>> f()
g
global
также может использоваться для вложенных функций. Помимо разрешения присвоения глобальной переменной, как в невложенной функции, это также можно использовать для доступа к глобальной переменной при наличии нелокальной переменной:
>>> def f():
... def g():
... global x
... print(x)
... x = "f"
... g()
...
>>> x = "global"
>>> f()
global
Для вложенных функций также существует nonlocal
объявление для присвоения нелокальной переменной, аналогично использованию global
в невложенной функции:
>>> def f():
... def g():
... nonlocal x # Python 3 only
... x = "g"
... x = "f"
... g()
... print(x)
...
>>> x = "global"
>>> f()
g
>>> print(x)
global
Р
[ редактировать ]R — это язык с лексической областью действия, в отличие от других реализаций S , где значения свободных переменных определяются набором глобальных переменных, тогда как в R они определяются контекстом, в котором была создана функция. [34] Доступ к контекстам области действия можно получить с помощью различных функций (таких как parent.frame()
), который может имитировать динамическую область видимости, если пожелает программист.
Нет области действия блока:
a <- 1
{
a <- 2
}
message(a)
## 2
Функции имеют доступ к области, в которой они были созданы:
a <- 1
f <- function() {
message(a)
}
f()
## 1
Переменные, созданные или измененные внутри функции, остаются там:
a <- 1
f <- function() {
message(a)
a <- 2
message(a)
}
f()
## 1
## 2
message(a)
## 1
Переменные, созданные или измененные внутри функции, остаются там, если явно не запрошено присвоение охватывающей области:
a <- 1
f <- function() {
message(a)
a <<- 2
message(a)
}
f()
## 1
## 2
message(a)
## 2
Хотя R по умолчанию имеет лексическую область видимости, области действия функций можно изменить:
a <- 1
f <- function() {
message(a)
}
my_env <- new.env()
my_env$a <- 2
f()
## 1
environment(f) <- my_env
f()
## 2
Примечания
[ редактировать ]- ^ См . определение значения «объема» и «контекста».
- ^ «Динамическая область» основывает разрешение имен на экстенте (сроке действия), а не на области , и поэтому формально является неточным.
- ^ Например, механизм шаблонов Jinja для Python по умолчанию использует как лексическую область (для импорта), так и динамическую область (для включений), а также позволяет указывать поведение с помощью ключевых слов; см. «Поведение контекста импорта» .
- ^ «Разрешение имен» и «привязка имен» во многом являются синонимами; узко говоря, «разрешение» определяет, к какому имени относится конкретное использование имени, не связывая его с каким-либо значением, как в абстрактном синтаксисе более высокого порядка , в то время как «привязка» связывает имя с фактическим значением. На практике эти термины используются как взаимозаменяемые.
- ^ Для самомодифицирующегося кода сам лексический контекст может меняться во время выполнения.
- ^ Напротив, * «контекст привязки имени», * «привязка имени, входящая в область действия» или * «привязка имени, выходящая за пределы области действия» неверны: привязка имени имеет область действия, а часть программы имеет контекст. .
Ссылки
[ редактировать ]- ^ «Отчет об алгоритмическом языке Алгол 60», 2.7. Количества, виды и области применения
- ^ WG14 N1256 (обновленная версия стандарта C99 2007 г. ), 6.2.1 Области действия идентификаторов, 7 сентября 2007 г.
- ^ Jump up to: а б Спецификация языка программирования Go : объявления и область применения , версия от 13 ноября 2013 г.
- ^ Jump up to: а б с Борнинг А. CSE 341 – Лексическая и динамическая область видимости . Университет Вашингтона.
- ^ Крокфорд, Дуглас. «Соглашения по коду для языка программирования JavaScript» . Проверено 4 января 2015 г.
- ^ Бэкус, Дж.В.; Вегштейн, Дж. Х.; Ван Вейнгаарден, А.; Вуджер, М.; Бауэр, Флорида; Грин, Дж.; Кац, К.; Маккарти, Дж.; Перлис, AJ; Рутисхаузер, Х.; Самельсон, К.; Вокуа, Б. (1960). «Отчет по алгоритмическому языку АЛГОЛ 60» . Коммуникации АКМ . 3 (5): 299. дои : 10.1145/367236.367262 . S2CID 278290 .
- ^ «Функции — Javascript:MDN» . 23 апреля 2023 г.
К переменным, определенным внутри функции, нельзя получить доступ откуда-либо за пределами функции, поскольку переменная определена только в области видимости функции. Однако функция может получить доступ ко всем переменным и функциям, определенным внутри области, в которой она определена.
- ^ «N4720: Рабочий проект, расширения C++ для модулей» (PDF) . Архивировано (PDF) из оригинала 30 апреля 2019 г. Проверено 30 апреля 2019 г.
- ^ « Прагматика языка программирования », таблица символов ЛеБланка-Кука
- ^ « Абстракция таблицы символов для реализации языков с явным контролем области действия », ЛеБланк-Кук, 1983
- ^ Луи Стил, Гай (август 1982 г.). «Обзор COMMON LISP». Материалы симпозиума ACM 1982 года по LISP и функциональному программированию - LFP '82 . стр. 98–107. дои : 10.1145/800068.802140 . ISBN 0-89791-082-6 . S2CID 14517358 .
- ^ Джоэл, Моисей (июнь 1970 г.). «Функция FUNCTION в LISP». Памятка MIT AI 199 . Лаборатория искусственного интеллекта Массачусетского технологического института.
- ^ Стил, Гай Льюис младший; Сассман, Джеральд Джей (май 1978 г.). «Искусство переводчика; или Комплекс модульности (нулевая, первая и вторая части)». Памятка MIT AI 453 . Лаборатория искусственного интеллекта Массачусетского технологического института.
- ^ Дрожь, Олин. «История Т» . Пол Грэм . Проверено 5 февраля 2020 г.
- ^ Стил, Гай Льюис младший (май 1978 г.). КРОЛИК: Компилятор СХЕМЫ (Технический отчет). Массачусетский технологический институт. hdl : 1721.1/6913 .
- ^ « Лексическая область видимости », Организация компьютеров и программ, Часть 3 , с. 18, в Google Книгах , Мичиганский университет. Летние инженерные конференции, 1967 г.
- ^ « Лексический обзор », Отчет о ходе реализации проекта MAC, том 8 , стр. 80, в Google Книгах , 1970 г.
- ^ Jump up to: а б Скотт 2009 , 3.4 Объем реализации, с. 143.
- ^ « Объем », XL C/C++ V8.0 для Linux, IBM
- ^ «Объявление переменных-членов (Учебные пособия по Java > Изучение языка Java > Классы и объекты)» . docs.oracle.com . Проверено 19 марта 2018 г.
- ^ «Управление доступом к членам класса (Учебные пособия по Java > Изучение языка Java > Классы и объекты)» . docs.oracle.com . Проверено 19 марта 2018 г.
- ^ « Все, что вам нужно знать об области видимости переменных Javascript », Саураб Паракх , «Программирование — это круто» , 8 февраля 2010 г.
- ^ «Аннотированный ES5» . es5.github.io . Проверено 19 марта 2018 г.
- ^ «Функции» . Веб-документы MDN . Проверено 19 марта 2018 г.
- ^ « 12.2 Оператор переменной », аннотированный ECMAScript 5.1, последнее обновление: 28 мая 2012 г.
- ^ « Обзор и подъем JavaScript », Бен Черри , Адекватно хорошо , 8 февраля 2010 г.
- ^ Замыкания Javascript , Ричард Корнфорд. март 2004 г.
- ^ « Объяснение области видимости и замыканий JavaScript », Роберт Найман, 9 октября 2008 г.
- ^ Питман, Кент (16 декабря 2007 г.). «Пересмотренное руководство Maclisp (The Pitmanual), воскресное утреннее издание» . MACLISP.info . Декларации HyperMeta Inc. и компилятор, понятие «переменные» . Проверено 20 октября 2018 г.
Если привязываемая переменная объявлена специальной, привязка компилируется как код, имитирующий способ привязки переменных интерпретатором.
- ^ Питман, Кент (16 декабря 2007 г.). «Пересмотренное руководство Maclisp (The Pitmanual), воскресное утреннее издание» . MACLISP.info . HyperMeta Inc. Оценщик, специальная форма
*FUNCTION
. Проверено 20 октября 2018 г.*FUNCTION
предназначен для решения « проблемы funarg », однако он работает только в некоторых простых случаях. - ^ Питман, Кент; и др. (сетевая версия стандарта ANSI X3.226-1994) (1996). «Common Lisp HyperSpec» . Lispworks.com . LispWorks Ltd. 1.1.2 История . Проверено 20 октября 2018 г.
MacLisp усовершенствовал понятие специальных переменных в Lisp 1.5... Основное влияние на Common Lisp оказали Lisp Machine Lisp, MacLisp, NIL, S-1 Lisp, Spice Lisp и Scheme.
- ^ «Язык программирования ISLISP, рабочий проект ISLISP 23.0» (PDF) . ISLISP.info . 11.1 Лексический принцип . Проверено 20 октября 2018 г.
Динамические привязки устанавливаются и доступны с помощью отдельного механизма (т. е.
defdynamic
,dynamic-let
, иdynamic
). - ^ «Лексическая привязка» . ЭмаксВики . Проверено 20 октября 2018 г.
В Emacs 24 имеется дополнительная лексическая привязка, которую можно включить для каждого буфера.
- ^ «Р FAQ» . cran.r-project.org . Проверено 19 марта 2018 г.
- Абельсон, Гарольд ; Сассман, Джеральд Джей ; Сассман, Джули (1996) [1984]. Структура и интерпретация компьютерных программ . Кембридж, Массачусетс: MIT Press . ISBN 0-262-51087-1 .
- Скотт, Майкл Л. (2009) [2000]. Прагматика языка программирования (Третье изд.). Издательство Морган Кауфманн. ISBN 978-0-12-374514-9 .
- Глава 3: Имена, области видимости и привязки, стр. 111–174.
- Раздел 13.4.1: Языки сценариев: инновационные функции: имена и области действия, стр. 691–699.