Область применения (информатика) - Scope (computer science)

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

Термин «объем» также используется для обозначения набора все привязки имен, которые действительны в рамках части программы или в данной точке программы, что более правильно называть контекст или Окружающая среда.[а]

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

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

Определение

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

АЛГОЛ 60 (1960)[1]
Различают следующие виды величин: простые переменные, массивы, метки, переключатели и процедуры. Объем количества - это набор операторов и выражений, в которых допустимо объявление идентификатора, связанного с этим количеством.
C (2007)[2]
Идентификатор может обозначать объект; функция; тег или член структуры, объединения или перечисления; а typedef имя; название ярлыка; имя макроса; или параметр макроса. Один и тот же идентификатор может обозначать разные объекты в разных точках программы. [...] Для каждого отдельного объекта, который обозначает идентификатор, идентификатор видимый (т.е. может использоваться) только в пределах области текста программы, называемой ее объем.
Идти (2013)[3]
Объявление связывает непустой идентификатор с константой, типом, переменной, функцией, меткой или пакетом. [...] Объем объявленного идентификатора - это объем исходного текста, в котором идентификатор обозначает указанную константу, тип, переменную, функцию, метку или пакет.

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

Лексическая область видимости против динамической области

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

Большинство современных языков используют лексическую область видимости для переменных и функций, хотя динамическая область видимости используется в некоторых языках, особенно в некоторых диалектах Лиспа, некоторых языках "сценариев" и некоторых языки шаблонов. [c] Perl 5 предлагает как лексическую, так и динамическую область видимости. Даже в языках с лексической областью видимости закрытие может сбивать с толку непосвященных, поскольку они зависят от лексического контекста, в котором определено замыкание, а не от того, где оно вызывается.

Лексическое разрешение можно определить на время компиляции, и также известен как раннее связывание, в то время как динамическое разрешение в целом можно определить только при время выполнения, и поэтому известен как позднее связывание.

Связанные понятия

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

Некоторые фреймворки программирования, такие как AngularJS используйте термин «область действия» для обозначения чего-то совершенно другого, чем то, что используется в этой статье. В этих фреймворках область видимости - это просто объект языка программирования, который они используют (JavaScript в случае AngularJS), который определенным образом используется фреймворком для имитации динамической области видимости на языке, который использует лексическую область видимости для своих переменных. Те Области действия AngularJS сами могут быть в контексте или не в контексте (используя обычное значение термина) в любой данной части программы, следуя обычным правилам области видимости переменных языка, как и любой другой объект, и используя свои собственные наследство и включение правила. В контексте AngularJS иногда используется термин «$ scope» (со знаком доллара), чтобы избежать путаницы, но руководства по стилю часто не рекомендуют использовать знак доллара в именах переменных.[5]

Использовать

Объем - важная составляющая разрешение имени,[d] что, в свою очередь, важно для языковая семантика. Разрешение имен (включая область действия) варьируется в зависимости от языка программирования, а внутри языка программирования зависит от типа объекта; правила для области действия называются правила области применения (или правила определения объема работ). Вместе с пространства имен правила области действия имеют решающее значение в модульное программирование, поэтому изменение одной части программы не нарушает работу несвязанной части.

Обзор

При обсуждении области применения можно выделить три основных понятия: объем, степень, и контекст. В частности, «область действия» и «контекст» часто путают: область действия - это свойство привязки имени, а контекст - это свойство части программы, которая является либо частью исходного кода (лексический контекст или статический контекст) или часть время выполнения (контекст исполнения, контекст выполнения, контекст вызова или динамический контекст). Контекст выполнения состоит из лексического контекста (в текущей точке выполнения) и дополнительного состояния выполнения, такого как стек вызовов.[e] Строго говоря, во время выполнения программа входит и выходит из областей различных привязок имен, и в момент выполнения привязки имен находятся «в контексте» или «не в контексте», следовательно, привязки имен «входят в контекст» или «выходят из контекста» "по мере того, как выполнение программы входит или выходит за пределы области.[f] Однако на практике использование гораздо менее жесткое.

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

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

Определение того, к какому объекту относится имя, известно как разрешение имени или привязка имени (особенно в объектно-ориентированного программирования ) и варьируется в зависимости от языка. Учитывая имя, язык (точнее, компилятор или интерпретатор) проверяет все сущности, которые находятся в контексте, на совпадения; в случае двусмысленности (два объекта с одинаковым именем, например глобальная и локальная переменные с одинаковым именем), правила разрешения имен используются для их различения. Чаще всего разрешение имен полагается на правило «внутреннего контекста во внешний», такое как правило Python LEGB (локальный, закрывающий, глобальный, встроенный): имена неявно разрешаются в наиболее узкий релевантный контекст. В некоторых случаях разрешение имени можно указать явно, например, с помощью Глобальный и нелокальный ключевые слова в Python; в других случаях правила по умолчанию не могут быть отменены.

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

Различный языки программирования имеют разные правила области видимости для разных типов объявлений и имен. Такие правила области видимости имеют большое влияние на языковая семантика и, как следствие, от поведения и корректности программ. На языках вроде C ++, доступ к несвязанной переменной не имеет четко определенной семантики и может привести к неопределенное поведение, аналогично обращению к висячий указатель; а объявления или имена, используемые вне их области видимости, будут генерировать синтаксические ошибки.

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

Уровни охвата

Объем может варьироваться от одного выражения до целой программы с множеством возможных градаций между ними. Самое простое правило области - это глобальная область видимости - все сущности видны во всей программе. Самым основным правилом модульной области видимости является двухуровневая область видимости, с глобальной областью видимости в любом месте программы и локальной областью внутри функции. Более сложное модульное программирование позволяет использовать отдельную область модуля, в которой имена видны внутри модуля (частные для модуля), но не видны за его пределами. Внутри функции некоторые языки, такие как C, позволяют области блока ограничивать область действия подмножеством функции; другие, особенно функциональные языки, позволяют ограничивать область действия выражением одним выражением. Другие области включают область действия файла (особенно в C), которая ведет себя аналогично области модуля, и область действия блока вне функций (особенно в Perl).

Тонкая проблема - когда именно начинается и заканчивается область действия. В некоторых языках, таких как C, область действия имени начинается с его объявления, и поэтому разные имена, объявленные в данном блоке, могут иметь разные области действия. Это требует объявления функций перед использованием, но не обязательно их определения, и требует предварительная декларация в некоторых случаях, особенно для взаимной рекурсии. В других языках, таких как JavaScript или Python, область действия имени начинается с начала соответствующего блока (например, начала функции), независимо от того, где он определен, и все имена в данном блоке имеют одинаковую область действия; в JavaScript это известно как переменный подъем. Однако, когда имя привязано к значению, меняется и поведение контекстных имен, имеющих неопределенное значение, отличается: в Python использование неопределенных имен приводит к ошибке выполнения, тогда как в JavaScript неопределенные имена, объявленные с вар (но не имена, объявленные с позволять ни const) можно использовать во всей функции, потому что они привязаны к значению неопределенный.

Объем выражения

Объем имени - это выражение, который известен как область выражения. Область выражения доступна на многих языках, особенно функциональный языки, которые предлагают функцию под названием let-выражения позволяя области объявления быть одним выражением. Это удобно, если, например, для вычисления требуется промежуточное значение. Например, в Стандартный ML, если f () возвращается 12, тогда пусть вал х = f () в х * х конец это выражение, которое оценивается как 144, используя временную переменную с именем Икс чтобы не звонить f () дважды. Некоторые языки с областью видимости блока приближают эту функциональность, предлагая синтаксис для встраивания блока в выражение; например, вышеупомянутое выражение Standard ML может быть записано на Perl так как делать { мой $ x = ж(); $ x * $ x }, или в GNU C так как ({ int Икс = ж(); Икс * Икс; }).

В Python вспомогательные переменные в выражениях генератора и понимания списков (в Python 3) имеют область выражения.

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

Область действия блока

Объем имени - это блокировать, который известен как область действия блока. Область видимости блока доступна во многих, но не во всех, языках программирования с блочной структурой. Это началось с АЛГОЛ 60, где "[e] самое объявление ... действительно только для этого блока.",[6] и сегодня особенно ассоциируется с языками в Паскаль и C семьи и традиции. Чаще всего этот блок содержится внутри функции, таким образом ограничивая область видимости частью функции, но в некоторых случаях, таких как Perl, блок может не находиться внутри функции.

беззнаковый int сумма квадратов(const беззнаковый int N) {  беззнаковый int Ret = 0;  для (беззнаковый int п = 1; п <= N; п++) {    const беззнаковый int n_squared = п * п;    Ret += n_squared;  }  вернуть Ret;}

Характерным примером использования области видимости блока является показанный здесь код C, где две переменные привязаны к циклу: переменная цикла п, который инициализируется один раз и увеличивается на каждой итерации цикла, а вспомогательная переменная n_squared, который инициализируется на каждой итерации. Цель состоит в том, чтобы избежать добавления переменных в область действия функции, которые имеют отношение только к определенному блоку - например, это предотвращает ошибки, когда общая переменная цикла я случайно уже установлено другое значение. В этом примере выражение п * п обычно не назначается вспомогательной переменной, а тело цикла просто записывается ret + = n * n но в более сложных примерах полезны вспомогательные переменные.

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

Тонкость нескольких языков программирования, таких как Алгол 68 и C (продемонстрировано в этом примере и стандартизировано, поскольку C99 ), заключается в том, что переменные области видимости блока могут быть объявлены не только в теле блока, но и в операторе управления, если таковой имеется. Это аналогично параметрам функции, которые объявляются в объявлении функции (перед запуском блока тела функции) и в области видимости всего тела функции. Это в основном используется в для петель, которые имеют оператор инициализации, отдельный от условия цикла, в отличие от циклов while, и являются общей идиомой.

Область видимости блока может использоваться для затенения. В этом примере внутри блока также могла быть вызвана вспомогательная переменная п, затеняя имя параметра, но это считается плохим стилем из-за вероятности ошибок. Кроме того, некоторые потомки C, такие как Java и C #, несмотря на поддержку области видимости блока (в том смысле, что локальная переменная может быть выведена из контекста до завершения функции), не позволяют одной локальной переменной скрывать другую. . На таких языках попытка провозглашения второго п приведет к синтаксической ошибке, и одна из п переменные пришлось бы переименовать.

Если для установки значения переменной используется блок, область действия блока требует, чтобы переменная была объявлена ​​вне блока. Это усложняет использование условных операторов с разовое задание. Например, в Python, который не использует область видимости блока, можно инициализировать переменную как таковую:

если c:    а = "фу"еще:    а = ""

где а доступен после если заявление.

В Perl, который имеет область видимости блока, вместо этого требуется объявление переменной перед блоком:

мой $ а;если (c) {    $ а = 'фу';} еще {    $ а = '';}

Часто это вместо этого переписывается с использованием множественного присваивания, инициализируя переменную значением по умолчанию. В Python (где это не обязательно) это будет:

а = ""если c:    а = "фу"

а в Perl это будет:

мой $ а = '';если (c) {    $ а = 'фу';}

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

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

суб increment_counter {    мой $ counter = 0;    вернуть суб    {        вернуть ++$ counter;    }}

Некоторые языки позволяют применять концепцию области блока в различной степени за пределами функции. Например, в фрагменте кода Perl справа $ counter имя переменной с областью видимости блока (из-за использования мой ключевое слово), а increment_counter - это имя функции с глобальной областью видимости. Каждый звонок increment_counter увеличит стоимость $ counter на единицу и верните новое значение. Код вне этого блока может вызывать increment_counter, но не может иным образом получить или изменить стоимость $ counter. Эта идиома позволяет определить закрытие в Perl.

Объем функции

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

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

def квадрат(п):    вернуть п * пdef сумма квадратов(п):    Всего = 0     я = 0    в то время как я <= п:        Всего += квадрат(я)        я += 1    вернуть Всего

Например, во фрагменте кода Python справа определены две функции: квадрат и сумма квадратов. квадрат вычисляет квадрат числа; сумма квадратов вычисляет сумму всех квадратов до числа. (Например, квадрат (4) это 42 = 16, и сумма_квадратов (4) 02 + 12 + 22 + 32 + 42 = 30.)

Каждая из этих функций имеет переменную с именем п который представляет аргумент функции. Эти двое п переменные полностью разделены и не связаны между собой, несмотря на то, что имеют одно и то же имя, потому что они являются локальными переменными с лексической областью видимости и областью действия функции: каждая область видимости является собственной, лексически отдельной функцией и, таким образом, они не перекрываются. Следовательно, сумма квадратов может позвонить квадрат без собственного п изменяется. Так же, сумма квадратов имеет переменные с именем Всего и я; эти переменные из-за их ограниченной области действия не будут мешать никаким переменным с именем Всего или я это может принадлежать любой другой функции. Другими словами, нет риска коллизия имен между этими именами и любыми не связанными именами, даже если они идентичны.

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

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

Область файла

Область имени - это файл, который известен как область файла. Область видимости файла в значительной степени специфична для C (и C ++), где область переменных и функций, объявленных на верхнем уровне файла (не внутри какой-либо функции), предназначена для всего файла или, скорее, для C, от объявления до конца исходный файл, а точнее единица перевода (внутренняя ссылка). Это можно рассматривать как форму области видимости модуля, где модули идентифицируются с файлами, а в более современных языках заменяется явной областью видимости модуля. Из-за наличия операторов include, которые добавляют переменные и функции во внутренний контекст и сами могут вызывать дополнительные операторы include, может быть трудно определить, что находится в контексте в теле файла.

В приведенном выше фрагменте кода C имя функции сумма квадратов имеет файловую область.

Объем модуля

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

В некоторых объектно-ориентированного программирования языков, в которых отсутствует прямая поддержка модулей, таких как C ++, аналогичная структура вместо этого предоставляется иерархией классов, где классы являются базовой единицей программы, а класс может иметь частные методы. Это правильно понимается в контексте динамическая отправка а не разрешение имен и объем, хотя они часто играют аналогичные роли. В некоторых случаях доступны обе эти возможности, например, в Python, в котором есть как модули, так и классы, а организация кода (как функция уровня модуля или традиционно закрытый метод) является выбором программиста.

Глобальный охват

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

Лексическая область видимости против динамической области

Использование локальных переменных - имен переменных с ограниченной областью действия, которые существуют только в пределах определенной функции - помогает избежать риска конфликта имен между двумя переменными с одинаковыми именами. Однако есть два очень разных подхода к ответу на этот вопрос: что значит находиться «внутри» функции?

В лексическая область (или лексическая область видимости; также называется статическая область видимости или статическая область видимости), если область видимости имени переменной является определенной функцией, то ее областью действия является текст программы определения функции: в этом тексте имя переменной существует и привязано к значению переменной, но вне этого текста имя переменной не существует. Напротив, в динамический диапазон (или динамическое определение объема), если областью действия имени переменной является определенная функция, то ее областью действия является период времени, в течение которого функция выполняется: пока функция выполняется, имя переменной существует и привязано к ее значению, но после того, как функция вернется , имя переменной не существует. Это означает, что если функция ж вызывает отдельно определенную функцию г, то в лексическом контексте функция г делает не иметь доступ к жлокальные переменные (при условии, что текст г не находится внутри текста ж), находясь в динамической области видимости, функция г делает иметь доступ к жлокальные переменные (поскольку г вызывается во время вызова ж).

$ # язык bash$ Икс=1$ функция г() { эхо $ x ; Икс=2 ; }$ функция ж() { местный Икс=3 ; г ; }$ ж # это выводит 1 или 3?3$ эхо $ x # выводит ли это 1 или 2?1

Рассмотрим, например, программу справа. Первая строка, Икс=1, создает глобальную переменную Икс и инициализирует его 1. Вторая строка, функция г() { эхо $ x ; Икс=2 ; }, определяет функцию г выводит ("эхо") текущее значение Икс, а затем устанавливает Икс к 2 (перезапись предыдущего значения). Третья строка, функция ж() { местный Икс=3 ; г ; } определяет функцию ж который создает локальную переменную Икс (скрывая глобальную переменную с таким же именем) и инициализирует ее 3, а затем звонит г. Четвертая строка, ж, звонки ж. Пятая строка, эхо $ x, распечатывает текущее значение Икс.

Итак, что именно печатает эта программа? Это зависит от правил области применения. Если язык этой программы использует лексическую область видимости, то г печатает и изменяет глобальную переменную Икс (потому что г определяется вне ж), поэтому программа печатает 1 а потом 2. Напротив, если этот язык использует динамическую область видимости, то г печатает и изменяет жлокальная переменная Икс (потому что г вызывается изнутри ж), поэтому программа печатает 3 а потом 1. (Как оказалось, язык программы Баш, который использует динамическую область видимости; поэтому программа печатает 3 а потом 1. Если тот же код был запущен с кш93 который использует лексическую область видимости, результаты будут другими.)

Лексический объем

С участием лексическая область, имя всегда относится к его лексическому контексту. Это свойство текста программы, независимое от времени выполнения. стек вызовов по языковой реализации. Поскольку это сопоставление требует только анализа статического текста программы, этот тип области также называется статическая область видимости. Лексический объем стандартен во всех АЛГОЛ на основе языков, таких как Паскаль, Модула-2 и Ада а также в современных функциональных языках, таких как ML и Haskell. Он также используется в Язык C и его синтаксические и семантические родственники, хотя и с различными ограничениями. Статическая область видимости позволяет программисту рассматривать ссылки на объекты, такие как параметры, переменные, константы, типы, функции и т. Д., Как простые подстановки имен. Это значительно упрощает создание модульного кода и его рассуждение, поскольку локальную структуру именования можно понять изолированно. Напротив, динамическая область видимости заставляет программиста предвидеть все возможные контексты выполнения, в которых может быть вызван код модуля.

программа А;вар я:целое число;    K:char;    процедура B;    вар K:настоящий;        L:целое число;        процедура C;        вар M:настоящий;        начать         (* объем A + B + C *)        конец;     (* объем A + B *)    конец; (* объем A *)конец.

Например, Паскаль имеет лексическую область видимости. Рассмотрим фрагмент программы на Паскале справа. Переменная я виден во всех точках, потому что он никогда не скрывается другой переменной с тем же именем. В char переменная K виден только в основной программе, потому что он скрыт настоящий переменная K видно в процедуре B и C только. Переменная L также видно только в процедуре B и C но он не скрывает никакой другой переменной. Переменная M видно только в процедуре C и поэтому недоступен ни из процедуры B или основная программа. Также процедура C видно только в процедуре B и поэтому не может быть вызван из основной программы.

Могла быть другая процедура C заявлено в программе вне процедуры B. Место в программе, где "C", затем определяет, какая из двух названных процедур C он представляет собой, таким образом, точно аналогичный объем переменных.

Правильная реализация лексической области в языках с первый класс вложенные функции не является тривиальным, поскольку требует, чтобы каждое значение функции несло с собой запись значений переменных, от которых оно зависит (пара функции и этого контекста называется закрытие ). В зависимости от реализации и компьютерная архитектура, переменная искать может стать немного неэффективным[нужна цитата ] когда очень глубоко лексически вложенный используются функции, хотя есть хорошо известные методы для смягчения этого.[7][8] Кроме того, для вложенных функций, которые ссылаются только на свои собственные аргументы и (непосредственно) локальные переменные, все относительные местоположения могут быть известны в время компиляции. Таким образом, при использовании этого типа вложенных функций не возникает никаких накладных расходов. То же самое относится к определенным частям программы, где вложенные функции не используются, и, естественно, к программам, написанным на языке, где вложенные функции недоступны (например, на языке C).

История

Лексическая область была использована для императивного языка АЛГОЛ 60 и с тех пор используется в большинстве других императивных языков.[4]

Такие языки, как Паскаль и C всегда имели лексический объем, поскольку оба они находились под влиянием идей, которые вошли в АЛГОЛ 60 и АЛГОЛ 68 (хотя C не включал лексически вложенные функции ).

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

Оригинал Лисп интерпретатор (1960) использовал динамическую область видимости. Глубокая привязка, который приближает статическую (лексическую) область видимости, был введен в LISP 1.5 (через Funarg устройство разработано Стив Рассел, работая под Джон Маккарти ).

Все рано Лиспы используется динамическая область видимости, по крайней мере, когда она основана на интерпретаторах. В 1982 году Гай Л. Стил-младший и Common LISP Group издают Обзор Common LISP,[9] краткий обзор истории и различных реализаций Lisp до того момента, а также обзор функций, которые Common Lisp реализация должна иметь. На странице 102 мы читаем:

Большинство реализаций LISP внутренне несовместимы в том смысле, что по умолчанию интерпретатор и компилятор могут назначать разную семантику для правильных программ; это происходит в первую очередь из-за того, что интерпретатор предполагает, что все переменные имеют динамическую область видимости, в то время как компилятор предполагает, что все переменные являются локальными, если не вынужден принять иное. Это было сделано для удобства и эффективности, но может привести к очень незаметным ошибкам. Определение Common LISP позволяет избежать таких аномалий, явно требуя, чтобы интерпретатор и компилятор налагали идентичную семантику на правильные программы.

Таким образом, реализация Common LISP должна была иметь лексическая область. Опять же, из Обзор Common LISP:

Вдобавок Common LISP предлагает следующие возможности (большинство из которых заимствовано из MacLisp, InterLisp или Lisp Machines Lisp): (...) Переменные с полной лексической областью видимости. Так называемая «проблема FUNARG»[10][11] полностью решена как в восходящем, так и в восходящем случаях.

К тому же году, в котором Обзор Common LISP был опубликован (1982), первоначальные проекты (также Гая Л. Стила младшего) скомпилированного Lisp с лексической областью видимости, названного Схема были опубликованы, и предпринимались попытки реализации компилятора. В то время обычно опасались, что лексическая область видимости в Лиспе будет неэффективной для реализации. В История Т,[12] Олин Шиверс пишет:

Все серьезные Лиспы, использовавшиеся в производстве в то время, имели динамическую область видимости. Никто, кто не внимательно прочитал Кролика[13] диссертация (написанная Гаем Льюисом Стилом-младшим в 1978 году) считала, что лексический охват будет летать; даже те немногие, кто было прочитав это, я немного поверил в то, что это будет работать в серьезном производственном использовании.

Термин «лексическая область» датируется как минимум 1967 г.,[14] в то время как термин «лексическая область видимости» датируется по крайней мере 1970 годом, когда он использовался в Проект MAC для описания правил области видимости диалекта Лиспа Лей (тогда известный как "Muddle").[15]

Динамический объем

С участием динамический диапазон, имя относится к контексту выполнения. Это необычно для современных языков.[4] С технической точки зрения это означает, что каждое имя имеет глобальный стек привязок. Представляем локальную переменную с именем Икс накладывает привязку на глобальный Икс стек (который мог быть пустым), который выскакивает, когда поток управления покидает область действия. Оценка Икс в любом контексте всегда дает верхнюю привязку. Обратите внимание, что это невозможно сделать во время компиляции, потому что стек привязки существует только в время выполнения, поэтому область видимости этого типа называется динамичный объем.

Как правило, определенные блоки определены для создания привязок, время жизни которых является временем выполнения блока; это добавляет некоторые функции статической области видимости к процессу динамической области. Однако, поскольку часть кода может быть вызвана из множества разных мест и ситуаций, может быть трудно определить с самого начала, какие привязки будут применяться при использовании переменной (или если она вообще существует). Это может быть полезно; применение принцип наименьшего знания предполагает, что код избегает зависимости от причины для (или обстоятельств) значения переменной, но просто используйте значение в соответствии с определением переменной. Такая узкая интерпретация общих данных может обеспечить очень гибкую систему для адаптации поведения функции к текущему состоянию (или политике) системы. Однако это преимущество основывается на тщательном документировании всех переменных, используемых таким образом, а также на тщательном избегании предположений о поведении переменной и не предоставляет никакого механизма для обнаружения интерференции между различными частями программы. Некоторые языки, например Perl и Common Lisp, позволяют программисту выбирать статическую или динамическую область видимости при определении или переопределении переменной. Примеры языков, использующих динамическую область видимости, включают Логотип, Emacs Lisp, Латекс и языки оболочки трепать, рывок, и PowerShell.

Реализовать динамическую область видимости довольно просто. Чтобы найти значение имени, программа может пройти по стеку времени выполнения, проверяя каждую запись активации (фрейм стека каждой функции) на значение имени. На практике это становится более эффективным за счет использования список ассоциаций, который представляет собой стек пар имя / значение. Пары помещаются в этот стек при каждом объявлении и выталкиваются всякий раз, когда переменные выходят из контекста.[16] Мелкая привязка это альтернативная стратегия, которая работает значительно быстрее и использует центральная справочная таблица, который связывает каждое имя со своим собственным набором значений. Это позволяет избежать линейного поиска во время выполнения для поиска конкретного имени, но следует позаботиться о том, чтобы правильно поддерживать эту таблицу.[16] Обратите внимание, что обе эти стратегии предполагают принцип «последним пришел - первым ушел» (LIFO ) упорядочивание привязок для любой одной переменной; на практике все привязки так упорядочены.

Еще более простая реализация - это представление динамических переменных с помощью простых глобальных переменных. Локальная привязка выполняется путем сохранения исходного значения в анонимном месте в стеке, невидимом для программы. Когда эта область привязки завершается, исходное значение восстанавливается из этого места. Фактически, таким образом возникла динамическая область видимости. Ранние реализации Лиспа использовали эту очевидную стратегию для реализации локальных переменных, и эта практика сохранилась в некоторых диалектах, которые все еще используются, таких как GNU Emacs Lisp. Лексическая область видимости была введена в Лисп позже. Это эквивалентно описанной выше схеме поверхностной привязки, за исключением того, что центральная справочная таблица является просто контекстом привязки глобальной переменной, в котором текущее значение переменной является ее глобальным значением. Поддерживать глобальные переменные не сложно. Например, объект символа может иметь выделенный слот для его глобального значения.

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

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

Макро-расширение

В современных языках расширение макроса в препроцессор является ключевым примером де-факто динамической области видимости. Сам макроязык только преобразует исходный код, не разрешая имена, но поскольку расширение выполняется на месте, когда имена в расширенном тексте затем разрешаются (особенно свободные переменные), они разрешаются в зависимости от того, где они раскрыты (свободно "называется"), как если бы происходила динамическая область видимости.

В Препроцессор C, используется для расширение макроса, имеет де-факто динамическую область видимости, так как не выполняет разрешение имен само по себе. Например, макрос:

#define ADD_A (x) x + a

будет расширяться, чтобы добавить а в переданную переменную, с этим именем только позже разрешенное компилятором на основе того, где макрос ДОБАВИТЬ "вызывается" (правильно, расширяется), находится в динамической области видимости и не зависит от того, где определен макрос. Собственно, препроцессор C выполняет только лексический анализ, расширяя макрос на этапе токенизации, но не разбирая его в синтаксическом дереве или выполняя разрешение имен.

Например, в следующем коде а в макросе разрешается (после расширения) в локальную переменную на сайте расширения:

#define ADD_A (x) x + aпустота добавить один(int *Икс) {  const int а = 1;  *Икс = ДОБАВИТЬ(*Икс);}пустота add_two(int *Икс) {  const int а = 2;  *Икс = ДОБАВИТЬ(*Икс);}

Полные имена

Как мы видели, одна из ключевых причин наличия области видимости заключается в том, что она помогает предотвратить конфликты имен, позволяя одинаковым именам ссылаться на разные объекты с ограничением, что имена должны иметь разные области действия. Иногда это ограничение неудобно; когда в программе должно быть доступно множество разных вещей, обычно всем им нужны имена с глобальной областью видимости, поэтому требуются разные методы, чтобы избежать конфликтов имен.

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

Хотя многие языки поддерживают эту концепцию, детали сильно различаются. В некоторых языках есть механизмы, например пространства имен в C ++ и C #, которые служат почти исключительно для организации глобальных имен в группы. В других языках есть механизмы, например пакеты в Ада и структуры в Стандартный ML, которые сочетают это с дополнительной целью, позволяющей видеть некоторые имена только другим членам их группы. И объектно-ориентированные языки часто позволяют классам или одноэлементным объектам выполнять эту задачу (независимо от того, также есть механизм, для которого это является основной целью). Более того, языки часто объединяют эти подходы; Например, Perl пакеты во многом похожи на пространства имен C ++, но могут использоваться как классы для объектно-ориентированного программирования; и Ява организует свои переменные и функции в классы, но затем организует эти классы в пакеты, подобные Ada.

По языку

Следуют правила содержания для репрезентативных языков.

C

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

Время жизни и видимость переменной определяются ее класс хранения. В C существует три типа времени жизни: статическое (выполнение программы), автоматическое (выполнение блока, выделенное в стеке) и ручное (выделенное в куче). Для переменных поддерживаются только статические и автоматические значения, которые обрабатываются компилятором, в то время как выделенная вручную память должна отслеживаться вручную для разных переменных. В C есть три уровня видимости: внешняя связь (глобальная), внутренняя связь (примерно файл) и область видимости блока (которая включает функции); области видимости блоков могут быть вложенными, и возможны различные уровни внутренней связи с использованием include. Внутренняя связь в C - это видимость на единица перевода уровень, а именно исходный файл после обработки Препроцессор C, в частности, включая все соответствующие включает.

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

В C переменные с областью видимости блока входят в контекст, когда они объявлены (не в верхней части блока), выходят из контекста, если какая-либо (не вложенная) функция вызывается внутри блока, возвращаются в контекст, когда функция возвращается, и выйти из контекста в конце блока. В случае автоматических локальных переменных они также выделяются при объявлении и освобождаются в конце блока, тогда как для статических локальных переменных они выделяются при инициализации программы и освобождаются при завершении программы.

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

#включают <stdio.h>int основной(пустота) {  char Икс = 'м';  printf("% c п", Икс);  {    printf("% c п", Икс);    char Икс = 'b';    printf("% c п", Икс);  }  printf("% c п", Икс);}

Программа выводит:

мммбм

В C. есть и другие уровни области видимости.[17] Имена переменных, используемые в прототипе функции, имеют видимость прототипа функции и контекст выхода в конце прототипа функции. Поскольку имя не используется, это бесполезно для компиляции, но может быть полезно для документации. Имена меток для оператора GOTO имеют область действия, а имена меток case для операторы переключения имеют блочную область видимости (блок переключателя).

C ++

Все переменные, которые мы собираемся использовать в программе, должны быть объявлены со спецификатором типа в более раннем пункте кода, как мы это делали в предыдущем коде в начале тела функции main, когда мы объявили, что a, b, и результат имели тип int. Переменная может иметь глобальную или локальную область видимости. Глобальная переменная - это переменная, объявленная в основном теле исходного кода, вне всех функций, а локальная переменная - это переменная, объявленная в теле функции или блока.

Современные версии позволять вложенная лексическая область видимости.

Swift

Swift имеет аналогичное правило для областей действия с C ++, но содержит другие модификаторы доступа.

МодификаторНепосредственный охватфайлСодержит модуль / пакетОстаток мира
открытодададаДа, позволяет подкласс
общественныйдададаДа, подкласс запрещен
внутреннийдададаНет
файлдадаНетНет
частныйдаНетНетНет

Идти

Идти лексически ограничен блоками.[3]

Ява

Ява лексически ограничен.

Класс Java может содержать три типа переменных:[18]

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

Как правило, набор скобок определяет конкретную область видимости, но переменные на верхнем уровне внутри класса могут различаться по своему поведению в зависимости от ключевых слов модификатора, используемых в их определении. В следующей таблице показан доступ к членам, разрешенный каждым модификатором.[19]

МодификаторКлассПакетПодклассМир
общественныйдададада
защищенныйдададаНет
(без модификатора)дадаНетНет
частныйдаНетНетНет

JavaScript

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

JavaScript имеет лексическую область видимости [21] вложены на уровне функции, причем глобальный контекст является самым внешним контекстом. Эта область используется как для переменных, так и для функций (что означает объявления функций, в отличие от переменных типа функции).[22] Заблокируйте прицел с помощью позволять и const ключевые слова стандартны, так как ECMAScript 6. Область действия блока может быть получена путем включения всего блока в функцию и последующего ее выполнения; это известно как немедленно вызываемое выражение функции (IIFE) шаблон.

Хотя область видимости JavaScript проста - лексическая, на уровне функций - связанные правила инициализации и разрешения имен вызывают путаницу. Во-первых, присвоение имени, не входящего в область видимости, по умолчанию означает создание новой глобальной переменной, а не локальной. Во-вторых, для создания новой локальной переменной необходимо использовать вар ключевое слово; затем в верхней части функции создается переменная со значением неопределенный и переменной присваивается свое значение, когда достигается выражение присваивания:

Переменная с Инициализатор присвоено значение его AssignmentExpression когда VariableStatement выполняется, а не при создании переменной.[23]

Это известно как переменный подъем[24]- объявление, но не инициализация, поднимается наверх функции. В-третьих, доступ к переменным перед инициализацией дает неопределенный, а не синтаксическая ошибка. В-четвертых, для объявлений функций объявление и инициализация поднимаются в верхнюю часть функции, в отличие от инициализации переменной. Например, следующий код создает диалог с выводом неопределенный, поскольку объявление локальной переменной поднимается, затеняя глобальную переменную, но инициализация не выполняется, поэтому переменная не определена при использовании:

а = 1;функция ж() {  предупреждение(а);  вар а = 2;}ж();

Кроме того, поскольку функции являются объектами первого класса в JavaScript и часто назначаются как обратные вызовы или возвращаются из функций, при выполнении функции разрешение имени зависит от того, где оно было изначально определено (лексический контекст определения), а не от лексического контекст или контекст выполнения, в котором он вызывается. Вложенные области действия конкретной функции (от наиболее глобальной до наиболее локальной) в JavaScript, в частности замыкания, используемого в качестве обратного вызова, иногда называют цепочка областей видимости, по аналогии с цепочкой прототипов объекта.

Закрытие могут быть созданы в JavaScript с использованием вложенных функций, поскольку функции являются объектами первого класса.[25] Возврат вложенной функции из включающей функции включает в себя локальные переменные включающей функции в качестве (нелокального) лексического контекста возвращаемой функции, что приводит к замыканию. Например:

функция newCounter() {  // возвращаем счетчик, который увеличивается при вызове (начиная с 0)  // и который возвращает новое значение  вар а = 0;  вар б = функция() { а++; вернуть а; };  вернуть б;}c = newCounter();предупреждение(c() + ' ' + c());  // выводит "1 2"

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

Разрешение имен свойств объектов JavaScript основано на наследовании в дереве прототипов - путь к корню в дереве называется цепочка прототипов—И не зависит от разрешения имен переменных и функций.

Лисп

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

Первоначальный Лисп использовал динамическую область видимости; это было Схема, вдохновленный АЛГОЛ, который ввел статическую (лексическую) область видимости в семейство Lisp.

Маклисп использует динамическую область видимости по умолчанию в интерпретаторе и лексическую область видимости по умолчанию в скомпилированном коде, хотя скомпилированный код может получить доступ к динамическим привязкам с помощью ОСОБЫЙ объявления для определенных переменных.[27] Однако, Маклисп рассматривал лексическое связывание больше как оптимизацию, чем можно было бы ожидать от современных языков, и оно не сопровождалось закрытие особенность, которую можно было бы ожидать от лексической области видимости в современных Лиспах. Отдельная операция, * ФУНКЦИЯ, был доступен для несколько неуклюжего решения некоторых из этих проблем.[28]

Common Lisp принял лексический объем от Схема,[29] как сделал Clojure.

ISLISP имеет лексическую область видимости для обычных переменных. В нем также есть динамические переменные, но они во всех случаях явно отмечены; они должны быть определены defdynamic особая форма, связанная динамичный специальная форма, и доступ к ней осуществляется явным динамичный особая форма.[30]

Некоторые другие диалекты Лиспа, например Emacs Lisp, по-прежнему использовать динамическую область видимости по умолчанию. Emacs Lisp теперь имеет лексическую область видимости, доступную для каждого буфера.[31]

Python

Для переменных Python имеет область действия функции, область видимости модуля и глобальную область видимости. Имена входят в контекст в начале области (функция, модуль или глобальная область) и выходят из контекста, когда вызывается невложенная функция или область заканчивается. Если имя используется до инициализации переменной, возникает исключение времени выполнения. Если к переменной просто осуществляется доступ (не присваивается ей), разрешение имен следует правилу LEGB (локальное, закрывающее, глобальное, встроенное), которое разрешает имена в самом узком релевантном контексте. Однако, если переменная назначена, по умолчанию объявляется переменная, область действия которой начинается в начале уровня (функции, модуля или глобального), а не в назначении. Оба эти правила могут быть отменены с помощью Глобальный или нелокальный (в Python 3) объявление перед использованием, которое позволяет получить доступ к глобальным переменным, даже если есть маскирующая нелокальная переменная, и назначить глобальные или нелокальные переменные.

В качестве простого примера функция преобразует переменную в глобальную область видимости:

>>> def ж():...     Распечатать(Икс)...>>> Икс = "Глобальный">>> ж()Глобальный

Обратите внимание, что Икс определяется до ж вызывается, поэтому ошибка не возникает, даже если она определена после ссылки в определении ж. Лексически это прямая ссылка, что разрешено в Python.

Здесь присваивание создает новую локальную переменную, которая не изменяет значение глобальной переменной:

>>> def ж():...     Икс = "е"...     Распечатать(Икс)...>>> Икс = "Глобальный">>> Распечатать(Икс)Глобальный>>> ж()ж>>> Распечатать(Икс)Глобальный

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

>>> def ж():...     Распечатать(Икс)...     Икс = "е"...>>> Икс = "Глобальный">>> ж()Отслеживание (последний вызов последний):  файл "", линия 1, в <module>  файл "", линия 2, в жUnboundLocalError: локальная переменная 'x', на которую ссылается перед присваиванием

Правила разрешения имен по умолчанию можно переопределить с помощью Глобальный или нелокальный (в Python 3) ключевые слова. В приведенном ниже коде глобальный x декларация в г Значит это Икс разрешается в глобальную переменную. Таким образом, к нему можно получить доступ (как это уже было определено), и присвоение присваивается глобальной переменной, а не объявлять новую локальную переменную. Обратите внимание, что нет Глобальный заявление необходимо в ж- поскольку он не присваивается переменной, по умолчанию он разрешается в глобальную переменную.

>>> def ж():...     Распечатать(Икс)...>>> def г():...     Глобальный Икс...     Распечатать(Икс)...     Икс = "г"...>>> Икс = "Глобальный">>> ж()Глобальный>>> г()Глобальный>>> ж()г

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

>>> def ж():...     def г():...         Глобальный Икс...         Распечатать(Икс)...     Икс = "е"...     г()...>>> Икс = "Глобальный">>> ж()Глобальный

Для вложенных функций также есть нелокальный объявление для присвоения нелокальной переменной, аналогично использованию Глобальный в невложенной функции:

>>> def ж():...     def г():...         нелокальный Икс  # Только Python 3...         Икс = "г"...     Икс = "е"...     г()...     Распечатать(Икс)...>>> Икс = "Глобальный">>> ж()г>>> Распечатать(Икс)Глобальный

р

р это язык с лексической областью видимости, в отличие от других реализаций S где значения свободных переменных определяются набором глобальных переменных, а в R они определяются контекстом, в котором была создана функция.[32] Доступ к контекстам области видимости можно получить с помощью различных функций (таких как parent.frame ()), который может имитировать динамическую область видимости, если программист пожелает.

Нет области блока:

а <- 1{  а <- 2}сообщение(а)## 2

Функции имеют доступ к области, в которой они были созданы:

а <- 1ж <- функция() {  сообщение(а)}ж()## 1

Переменные, созданные или измененные внутри функции, остаются там:

а <- 1ж <- функция() {  сообщение(а)  а <- 2  сообщение(а)}ж()## 1## 2сообщение(а)## 1

Переменные, созданные или измененные внутри функции, остаются там, если явно не запрашивается назначение охватывающей области:

а <- 1ж <- функция() {  сообщение(а)  а <<- 2  сообщение(а)}ж()## 1## 2сообщение(а)## 2

Хотя R по умолчанию имеет лексическую область видимости, области действия функций могут быть изменены:

а <- 1ж <- функция() {  сообщение(а)}my_env <- new.env()my_env$а <- 2ж()## 1Окружающая среда(ж) <- my_envж()## 2

Смотрите также

Заметки

  1. ^ Увидеть определение для значения «объема» по сравнению с «контекстом».
  2. ^ «Динамическая область видимости» основывает разрешение имен на степень (время жизни), а не объем, и поэтому формально неточен.
  3. ^ Например, Джиндзя механизм шаблонов для Python по умолчанию использует как лексическую область видимости (для импорта), так и динамическую область видимости (для включения), и позволяет определять поведение с помощью ключевых слов; увидеть Импортировать поведение контекста.
  4. ^ «Разрешение имени» и «привязка имени» в значительной степени синонимы; узко говоря, «разрешение» определяет, к какому имени относится конкретное использование имени, не связывая его с каким-либо значением, как в абстрактный синтаксис высшего порядка, а «привязка» связывает имя с реальным значением. На практике эти термины используются как синонимы.
  5. ^ Для самомодифицирующийся код сам лексический контекст может изменяться во время выполнения.
  6. ^ Напротив, * «контекст привязки имени», * «привязка имени входит в область видимости» или * «привязка имени выходит за пределы области действия» - все это неверно - привязка имени имеет область действия, а часть программы имеет контекст.

использованная литература

  1. ^ «Отчет об алгоритмическом языке Алгол 60», 2.7. Количество, виды и объемы
  2. ^ WG14 N1256 (Обновленная версия 2007 г. C99 стандарт), 6.2.1 Области действия идентификаторов, 2007-09-07
  3. ^ а б Спецификация языка программирования Go: Заявления и объем, Версия от 13 ноября 2013 г.
  4. ^ а б c Борнинг А. CSE 341 - Лексическая и динамическая область видимости. Вашингтонский университет.
  5. ^ Крокфорд, Дуглас. «Соглашения о коде для языка программирования JavaScript». Получено 2015-01-04.
  6. ^ Backus, J. W .; Wegstein, J. H .; Van Wijngaarden, A .; Woodger, M .; Bauer, F. L .; Green, J .; Katz, C .; McCarthy, J .; Perlis, A.J .; Rutishauser, H .; Самельсон, К .; Вокуа, Б. (1960). «Отчет по алгоритмическому языку АЛГОЛ 60». Коммуникации ACM. 3 (5): 299. Дои:10.1145/367236.367262. S2CID  278290.
  7. ^ "Прагматика языка программирования ", Таблица символов ЛеБланка-Кука
  8. ^ "Абстракция таблицы символов для реализации языков с явным управлением областью видимости ", ЛеБланк-Кук, 1983 г.
  9. ^ Луи Стил, Гай (август 1982 г.). «Обзор Common LISP». LFP '82: Материалы симпозиума ACM 1982 года по LISP и функциональному программированию: 98–107. Дои:10.1145/800068.802140. ISBN  0897910826. S2CID  14517358.
  10. ^ Иоиль, Моисей (июнь 1970 г.). «Функция FUNCTION в LISP». MIT AI Memo 199. Лаборатория искусственного интеллекта Массачусетского технологического института.
  11. ^ Стил, Гай Льюис-младший; Сассман, Джеральд Джей (май 1978 г.). «Искусство переводчика или комплекс модульности (части ноль, один и два)». MIT AI Memo 453. Лаборатория искусственного интеллекта Массачусетского технологического института.
  12. ^ Дрожит, Олин. "История Т". Пол Грэм. Получено 5 февраля 2020.
  13. ^ Стил, Гай Льюис-младший (май 1978 г.). «КРОЛИК: Компилятор для СХЕМЫ». Массачусетский технологический институт. HDL:1721.1/6913. Цитировать журнал требует | журнал = (Помогите)
  14. ^ "лексическая область ", Компьютерная и программная организация, Часть 3, п. 18, в Google Книги, Университет Мичигана. Летние инженерные конференции, 1967
  15. ^ "лексическая область видимости ", Отчет о ходе реализации проекта MAC, Том 8, п. 80, в Google Книги, 1970.
  16. ^ а б Скотт 2009, 3.4 Объем реализации, стр. 143.
  17. ^ "Объем ", XL C / C ++ V8.0 для Linux, IBM
  18. ^ «Объявление переменных-членов (Руководства по Java ™> Изучение языка Java> Классы и объекты)». docs.oracle.com. Получено 19 марта 2018.
  19. ^ «Управление доступом к членам класса (Руководства по Java ™> Изучение языка Java> Классы и объекты)». docs.oracle.com. Получено 19 марта 2018.
  20. ^ "Все, что вам нужно знать об области видимости переменных Javascript ", Саураб Парах, Кодирование - это круто, 2010-02-08
  21. ^ «Аннотированный ES5». es5.github.io. Получено 19 марта 2018.
  22. ^ «Функции». Веб-документы MDN. Получено 19 марта 2018.
  23. ^ "12.2 Заявление о переменной ", Аннотированный ECMAScript 5.1, последнее обновление: 28 мая 2012 г.
  24. ^ "Область видимости и подъема JavaScript ", Бен Черри, Достаточно хорошо, 2010-02-08
  25. ^ Замыкания Javascript, Ричард Корнфорд. Март 2004 г.
  26. ^ "Объяснение области действия и замыканий JavaScript ", Роберт Найман, 9 октября 2008 г.
  27. ^ Питман, Кент (16 декабря 2007 г.). «Пересмотренное руководство Maclisp (The Pitmanual), издание Sunday Morning». MACLISP.info. Декларации HyperMeta Inc. и компилятор, понятие «переменные». Получено 20 октября, 2018. Если связываемая переменная была объявлена ​​специальной, привязка компилируется как код, имитирующий способ привязки переменных интерпретатором.
  28. ^ Питман, Кент (16 декабря 2007 г.). «Пересмотренное руководство Maclisp (The Pitmanual), издание Sunday Morning». MACLISP.info. HyperMeta Inc. Оценщик, специальная форма * ФУНКЦИЯ. Получено 20 октября, 2018. * ФУНКЦИЯ предназначен для решения "проблема Funarg, ”Однако это работает только в некоторых простых случаях.
  29. ^ Питман, Кент; и другие. (веб-версия стандарта 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.
  30. ^ "Язык программирования ISLISP, рабочий проект ISLISP 23.0" (PDF). ISLISP.info. 11.1 Лексический принцип. Получено 20 октября, 2018. Динамические привязки устанавливаются и доступны через отдельный механизм (т. Е. defdynamic, динамичный, и динамичный).
  31. ^ «Лексическая привязка». EmacsWiki. Получено 20 октября, 2018. Emacs 24 имеет необязательную лексическую привязку, которую можно включить для каждого буфера.
  32. ^ "R FAQ". cran.r-project.org. Получено 19 марта 2018.