Қолдану аясы (информатика) - Scope (computer science)

Жылы компьютерлік бағдарламалау, ауқымы а атауы міндетті - атауды ұйымға байланыстыру, мысалы айнымалы - а. Бөлігі бағдарлама бұл жерде атау міндетті түрде жарамды, яғни бұл атауды ұйымға сілтеме жасау үшін қолдануға болады. Бағдарламаның басқа бөліктерінде атау басқа ұйымға сілтеме жасауы мүмкін (оның басқа байланысы болуы мүмкін), немесе ештеңеге қатысты болмауы мүмкін (ол шектеусіз болуы мүмкін). Атауды қолдану аясы көріну ұйымның, әсіресе ескі немесе одан да көп техникалық әдебиеттерде - бұл сілтеме атауы емес, сілтеме жасалған субъект тұрғысынан.

«Ауқым» термині жиынтыққа сілтеме жасау үшін де қолданылады барлық бағдарламаның бір бөлігінде немесе бағдарламаның белгілі бір нүктесінде жарамды, ол неғұрлым дұрыс деп аталатын байланыстырушылар контекст немесе қоршаған орта.[a]

Қатаң түрде[b] және іс жүзінде бағдарламалау тілдерінің көпшілігінде «бағдарламаның бөлігі» деген бөлікке жатады бастапқы код (мәтін аумағы), және ретінде белгілі лексикалық аясы. Алайда кейбір тілдерде «бағдарламаның бөлігі» жұмыс уақытының бір бөлігін білдіреді (орындау кезіндегі уақыт кезеңі) және « динамикалық ауқым. Бұл екі термин де біршама жаңылыстырады - оларда айтылғандай техникалық терминдерді дұрыс қолданбайды анықтама - бірақ айырмашылықтың өзі дәл және дәл, және бұл стандартты сәйкес терминдер. Лексикалық ауқым - бұл мақаланың негізгі өзегі, оның лексикалық аясынан айырмашылығы бар динамикалық аясы.

Көп жағдайда, аты-жөні лексикалық ауқымға негізделе отырып қолдану және қолдану салыстырмалы түрде қарапайым, өйткені қолдануда атау қай субъектіге сілтеме жасайтынын анықтау үшін бастапқы кодтан артқа оқуға болады, ал іске асыруда компиляцияны құрастыру немесе түсіндіру кезінде аттар мен мәнмәтіндер тізімін жүргізуге болады. бағдарлама. Қиындықтар пайда болады атын бүркемелеу, тікелей декларациялар, және көтеру, ал айтарлықтай нәзіктер пайда болады жергілікті емес айнымалылар, әсіресе жабылу.

Анықтама

Есімнің (лексикалық) «аясының» қатаң анықтамасы (идентификатор ) бір мағыналы - бұл «атаудың ұйыммен байланысы қолданылатын бастапқы кодтың бөлігі» және оның сипаттамасында 1960 анықтамасынан іс жүзінде өзгермеген ALGOL 60. Репрезентативті тіл ерекшеліктері орындалады.

ALGOL 60 (1960)[1]
Шамалардың келесі түрлері ажыратылады: қарапайым айнымалылар, массивтер, белгілер, ажыратқыштар және процедуралар. Шаманың ауқымы дегеніміз - осы шамамен байланысты сәйкестендірушінің декларациясы жарамды болатын сөйлемдер мен өрнектер жиынтығы.
C (2007)[2]
Идентификатор объектіні белгілей алады; функция; құрылымның, одақтың немесе тізімдеудің белгісі немесе мүшесі; а typedef аты; затбелгі атауы; макро атау; немесе макропараметр. Бір идентификатор бағдарламаның әртүрлі нүктелеріндегі әр түрлі нысандарды белгілей алады. [...] Идентификатор белгілейтін әр түрлі объект үшін идентификатор болып табылады көрінетін (яғни қолдануға болады) тек оның мәтін деп аталатын бағдарлама мәтінінің аймағында ауқымы.
Барыңыз (2013)[3]
Декларация бос емес идентификаторды тұрақтыға, түрге, айнымалыға, функцияға, затбелгіге немесе бумаға байланыстырады. [...] Жарияланған идентификатордың қолданылу саласы - бұл идентификатор көрсетілген тұрақты, тип, айнымалы, функция, затбелгі немесе буманы білдіретін бастапқы мәтіннің көлемі.

Көбінесе «ауқым» берілген аттың берілгенге сілтеме жасай алатын уақытын білдіреді айнымалы - қашан декларация әсер етеді - сонымен қатар функциялар, типтер, сыныптар сияқты басқа субъектілерге де қатысты болуы мүмкін жапсырмалар, тұрақтылар және санаулар.

Лексикалық ауқым мен динамикалық ауқым

«Бағдарламаның бір бөлігі» нені білдіреді. Тілдерінде лексикалық аясы (деп те аталады статикалық ауқым), атаудың ажыратымдылығы бастапқы кодтағы орынға және лексикалық контекст (деп те аталады статикалық контекст), ол көрсетілген айнымалы немесе функция анықталған жерде анықталады. Керісінше, тілдерінде динамикалық ауқым атаудың шешімі тәуелді болады бағдарлама күйі кезде анықталатын атау кездескенде орындау мазмұны (деп те аталады жұмыс уақыты контексті, контексті шақыру немесе динамикалық контекст). Іс жүзінде, лексикалық ауқыммен атау жергілікті лексикалық контекстті іздеу арқылы шешіледі, егер бұл сыртқы лексикалық контекстті іздеу арқылы сәтсіздікке ұшыраса және т.с.с., ал динамикалық ауқымда атау жергілікті орындау контекстін іздеу арқылы шешіледі, егер ол сыртқы орындалу мәтінмәнін іздеу арқылы сәтсіздікке ұшырайды және т.б.[4]

Қазіргі тілдердің көпшілігі лексикалық ауқымды айнымалылар мен функциялар үшін пайдаланады, дегенмен динамикалық ауқым кейбір тілдерде қолданылады, атап айтқанда Лисптің кейбір диалектілерінде, кейбір «сценарийлерде» және кейбіреулерінде шаблон тілдері. [c] Perl 5 лексикалық және динамикалық ауқымды ұсынады. Тіпті лексикалық ауқымды тілдерде де жабылу түсініксіздермен шатастыруы мүмкін, өйткені бұлар жабылу деп аталатын жерде емес, анықталатын лексикалық контекстке байланысты.

Лексикалық шешімді мына уақытта анықтауға болады жинақтау уақыты, және сондай-ақ ретінде белгілі ерте міндеттеу, ал динамикалық ажыратымдылықты жалпы жағдайда тек анықтауға болады жұмыс уақыты, және, осылайша, ретінде белгілі кеш байланыстыру.

Байланысты ұғымдар

Жылы объектіге бағытталған бағдарламалау, динамикалық диспетчер нысанды таңдайды әдіс орындалу кезінде, бірақ нақты атпен байланысу компиляция кезінде немесе орындалу уақытында жасалуы тілге байланысты. Іс жүзінде динамикалық ауқым кең таралған макро тілдер, бұл атауды тікелей жасамайды, бірақ орнына кеңейтіледі.

Кейбір бағдарламалау шеңберлері AngularJS «ауқым» терминін осы мақалада қалай қолданылатынынан мүлде өзгеше мағынада қолданыңыз. Бұл шеңберлерде қолдану аясы тек бағдарламалау тілінің объектісі болып табылады (JavaScript бұл белгілі бір жолдармен, оның айнымалылары үшін лексикалық шеңберді қолданатын тілдегі динамикалық ауқымды еліктеу үшін қолданылады. Анау AngularJS ауқымы бағдарламаның кез-келген бөлігінде контексте болуы немесе контексте болмауы мүмкін (терминнің әдеттегі мағынасын қолдана отырып), кез-келген басқа объект сияқты тілдің айнымалы ауқымының әдеттегі ережелерін сақтай отырып және өзінің мұрагерлік және трансклюзия ережелер. AngularJS контекстінде кейде шатасушылықты болдырмау үшін «доллар ауқымы» термині қолданылады (доллар белгісімен), бірақ айнымалы атауларында доллар белгісін қолдану көбінесе стиль нұсқаулықтарынан бас тартады.[5]

Пайдаланыңыз

Ауқымы - бұл маңызды компонент аты-жөні,[d] бұл өз кезегінде тілдік семантика. Атаудың ажыратымдылығы (қолдану аясын қоса алғанда) бағдарламалау тілдері арасында өзгереді, ал бағдарламалау тілінде құрылым түріне қарай әр түрлі болады; қолдану ережелері деп аталады қолдану ережелері (немесе аумақтық ережелер). Бірге атаулар кеңістігі, қолдану ережелері өте маңызды модульдік бағдарламалау, сондықтан бағдарламаның бір бөлігінің өзгеруі байланысты емес бөлікті бұзбайды.

Шолу

Көлемді талқылау кезінде үш негізгі ұғым бар: ауқымы, дәрежесі, және контекст. «Қолдану аясы» мен «контекст» жиі шатастырылады: ауқым - бұл атауды байланыстыратын қасиет, ал мәтінмән - бұл бағдарламаның бір бөлігі, яғни бастапқы кодтың бөлігі (лексикалық контекст немесе статикалық контекст) немесе оның бөлігі жұмыс уақыты (орындау мазмұны, жұмыс уақыты контексті, контексті шақыру немесе динамикалық контекст). Орындау мәтінмәні лексикалық контексттен (ағымдағы орындалу нүктесінде) және қосымша жұмыс уақытының күйінен тұрады шақыру стегі.[e] Қысқаша айтқанда, орындау барысында бағдарлама атаулардың әр түрлі ауқымына енеді және шығады, ал орындалу нүктесінде аттың байланысы «контекстте» немесе «контекстте емес» болады, демек, атпен байланыстыру «контекстке енеді» немесе «контексттен шығады» «өйткені бағдарламаның орындалуы ауқымға енеді немесе шығады.[f] Алайда іс жүзінде қолдану әлдеқайда бос.

Қолдану аясы - бұл дереккөз коды деңгейінің тұжырымдамасы және атауды байланыстыру қасиеті, әсіресе айнымалы немесе функционалды атаудың байланысы - бастапқы кодтағы аттар сілтемелер бағдарламадағы тұлғаларға - және тілдің компиляторы немесе аудармашысының мінез-құлқының бөлігі болып табылады. Осылайша, ауқым мәселелері ұқсас көрсеткіштер, бұл жалпы бағдарламаларда қолданылатын сілтеме түрі. Атау контекстте болған кезде, бірақ айнымалы инициализацияланбаған кезде айнымалының мәнін пайдалану а-ны ажырату (мәніне қол жеткізу) сияқты жабайы нұсқағыш, бұл анықталмағандықтан. Алайда, айнымалылар контекстен шыққанға дейін жойылмайтындықтан, а-ның аналогы ілулі көрсеткіш жоқ.

Айнымалылар сияқты нысандар үшін ауқымның жиынтығы болып табылады өмір кезеңі (сонымен бірге дәрежесі ) - атау тек бар айнымалыны (мүмкін анықталмаған мәнімен) байланыстыра алады, бірақ бар айнымалылар міндетті түрде көрінбейді: айнымалы болуы мүмкін, бірақ қол жетімді емес (мән сақталады, бірақ берілген контекстте айтылмайды), немесе қол жетімді, бірақ берілген атау арқылы емес, бұл жағдайда ол контексте болмайды (бағдарлама «атау аясынан тыс»). Басқа жағдайларда, «өмір бойы» маңызды емес - затбелгі (бастапқы кодтағы атауы) бағдарламамен бірдей өмір сүреді (статикалық түрде құрастырылған тілдер үшін), бірақ контекстте болуы мүмкін немесе бағдарламаның берілген нүктесінде болмайды, сол сияқты статикалық айнымалылар —А статикалық глобалды айнымалы бүкіл бағдарламаның контекстінде, ал а статикалық жергілікті айнымалы тек функцияның немесе басқа локальды контексттің контекстінде болады, бірақ екеуі де бағдарламаның бүкіл жұмыс істеу мерзіміне ие.

Атаудың қай тұлғаға қатысты екенін анықтау белгілі аты-жөні немесе атауы міндетті (әсіресе объектіге бағытталған бағдарламалау ), және тілдер арасында әр түрлі болады. Атау берілгендіктен, тіл (компилятор немесе аудармашы) контекстегі барлық нысандарды сәйкестікке тексереді; екіұштылық жағдайында (бірдей атауы бар екі тұлға, мысалы, бірдей және сол сияқты глобалды және жергілікті айнымалы), оларды ажырату үшін атауды анықтау ережелері қолданылады. Көбінесе, атаудың шешімі Python LEGB (Local, Enclosing, Global, built-in) ережесі сияқты «ішкі-сыртқы контекст» ережелеріне сүйенеді: аттар нақты тар контекстке сәйкес шешіледі. Кейбір жағдайларда атауды анықтауға болады, мысалы ғаламдық және жергілікті емес Python-дағы кілт сөздер; басқа жағдайларда әдепкі ережелерді жоққа шығаруға болмайды.

Екі бірдей атау контексте бір уақытта, әр түрлі тұлғаларға сілтеме жасағанда, біреу айтады атын бүркемелеу пайда болады, мұнда жоғары басымдылық атауы (әдетте ішкі жағы) төменгі басымдылық атын «бүркемелейді». Айнымалылар деңгейінде бұл ретінде белгілі ауыспалы көлеңке. Әлеуетіне байланысты логикалық қателер маскировкадан, кейбір тілдер маскировкаға рұқсат бермейді немесе кедергі жасайды, қате немесе ескерту жасау кезінде немесе ескерту кезінде.

Әр түрлі бағдарламалау тілдері әртүрлі декларациялар мен атауларға арналған әр түрлі әр түрлі ережелер бар. Мұндай ауқым ережелері үлкен әсер етеді тілдік семантика және, демек, бағдарламалардың тәртібі мен дұрыстығы туралы. Сияқты тілдерде C ++, шектелмеген айнымалыға қатынаудың семантикасы анықталмаған және нәтижеге әкелуі мүмкін анықталмаған мінез-құлық, а сілтемесіне ұқсас ілулі көрсеткіш; және олардың шеңберінен тыс қолданылған декларациялар немесе атаулар пайда болады синтаксистік қателер.

Ауқымдар көбінесе басқа тілдік құрылымдарға байланады және анықталмаған түрде анықталады, бірақ көптеген тілдер сонымен қатар ауқымды бақылау үшін арнайы құрылымдар ұсынады.

Қолдану деңгейлері

Қолдану аясы бір ғана өрнектен бастап бүкіл бағдарламаға дейін өзгеруі мүмкін, олардың арасында көптеген градациялар болуы мүмкін. Қарапайым ауқым ережесі - ғаламдық ауқым - барлық субъектілер бүкіл бағдарламада көрінеді. Ең негізгі модульдік ереже - бұл бағдарламаның кез-келген жерінде ғаламдық ауқыммен және жергілікті ауқыммен жұмыс істейтін екі деңгейлі ауқым. Неғұрлым жетілдірілген модульдік бағдарламалау модуль ішінде жеке көрінетін, бірақ модульден тыс көрінбейтін модульдің жеке ауқымын қамтамасыз етеді. Функцияның ішінде кейбір тілдер, мысалы, С, блоктың ауқымын функцияның ішкі жиынтығымен шектеуге мүмкіндік береді; басқалары, атап айтқанда функционалды тілдер, экспрессия аясын бір ғана өрнекпен шектеуге мүмкіндік береді. Басқа ауқымдарға модуль ауқымына ұқсас әрекет ететін файл ауқымы (атап айтқанда С тілінде) және функциялардан тыс ауқымды блоктау кіреді (атап айтқанда Perl-де).

Жіңішке мәселе - бұл ауқым басталып, аяқталған кезде. Кейбір тілдерде, мысалы, С-да, атаудың қолданылу аясы оны жариялаудан басталады, сол себепті берілген блокта жарияланған әр түрлі аттардың ауқымы әр түрлі болуы мүмкін. Бұл функцияларды қолданар алдында декларациялауды қажет етеді, бірақ оларды анықтау міндетті емес және қажет етеді алға қарай декларациялау кейбір жағдайларда, атап айтқанда өзара рекурсияға арналған. Басқа тілдерде, мысалы, JavaScript немесе Python, атаудың ауқымы тиісті блоктың басталуынан басталады (мысалы, функцияның басталуы), қай жерде анықталғанына қарамастан, және берілген блоктағы барлық атаулардың аясы бірдей; JavaScript-те бұл белгілі ауыспалы көтергіш. Алайда, атау мәнге байланысты болған кезде өзгереді және анықталмаған мәнге ие контексттегі атаулардың әрекеті әр түрлі болады: Python-да анықталмаған атауларды пайдалану орындалу кезінде қате жібереді, ал JavaScript-те анықталмаған атаулар var (бірақ бірге жарияланған атаулар емес рұқсат етіңіз не const) функциясы бойынша жарамды, өйткені олар мәнге байланысты белгісіз.

Өрнек ауқымы

Атаудың мәні: өрнек, ретінде белгілі өрнек аясы. Өрнек аясы көптеген тілдерде, әсіресе функционалды деп аталатын функцияны ұсынатын тілдер өрнектер декларацияның ауқымы бір өрнек болуына мүмкіндік беру. Бұл, мысалы, есептеу үшін аралық мән қажет болса, ыңғайлы. Мысалы, in Стандартты ML, егер f () қайтарады 12, содан кейін вал болсын x = f () жылы x * x Соңы деп бағалайтын өрнек болып табылады 144, деп аталатын уақытша айнымалыны қолданады х қоңырау шалмас үшін f () екі рет. Блоктың ауқымы бар кейбір тілдер бұл функцияны блоктың өрнекке енуіне синтаксис ұсына отырып жақындатады; мысалы, жоғарыда келтірілген ML стандартты өрнегін жазуға болады Перл сияқты істеу { менің $ x = f(); $ x * $ x }, немесе in GNU C сияқты ({ int х = f(); х * х; }).

Python-да генератор өрнектеріндегі көмекші айнымалылар және тізімді түсіну (Python 3-те) экспрессия ауқымына ие.

С-де, а-дағы айнымалы атаулар функционалдық прототип осы тұрғыда белгілі экспрессия аясына ие функционалдық хаттама аясы. Прототиптегі айнымалы атауларға сілтеме жасалмағандықтан (олар нақты анықтамада әр түрлі болуы мүмкін) - олар жай муляждар болып табылады, бірақ олар, мысалы, құжаттама жасау үшін қолданылуы мүмкін, жиі алынып тасталады.

Блоктың ауқымы

Атаудың мәні - а блок, ретінде белгілі блоктың ауқымы. Блоктың ауқымы көптеген, бірақ барлық емес, құрылымдық бағдарламаланған тілдерде бар. Бұл басталды ALGOL 60, онда «[e] декларация ... тек сол блок үшін жарамды.»,[6] және бүгінгі таңдағы тілдермен ерекше байланысты Паскаль және C отбасылар мен дәстүрлер. Көбінесе бұл блок функцияның ішінде болады, осылайша функцияның бір бөлігімен шектеледі, бірақ кейбір жағдайларда, мысалы, Perl, блок функцияның ішінде болмауы мүмкін.

қол қойылмаған int квадраттардың қосындысы(const қол қойылмаған int N) {  қол қойылмаған int рет = 0;  үшін (қол қойылмаған int n = 1; n <= N; n++) {    const қол қойылмаған int n_квадрат = n * n;    рет += n_квадрат;  }  қайту рет;}

Блок масштабын пайдаланудың репрезентативті мысалы - мұнда көрсетілген C коды, мұнда циклге екі айнымалылар жатады: цикл айнымалысы n, ол инициализацияланады және циклдің әр қайталануында өседі және көмекші айнымалы n_квадратәр итерация кезінде инициализацияланады. Мақсат функциялар ауқымына тек белгілі бір блокқа ғана қатысты айнымалыларды қосудан аулақ болу - мысалы, бұл жалпы цикл айнымалысындағы қателіктердің алдын алады мен кездейсоқ басқа мәнге қойылған. Бұл мысалда өрнек n * n әдетте көмекші айнымалыға берілмейді, ал цикл денесі жай жазылады ret + = n * n бірақ күрделі мысалдарда көмекші айнымалылар пайдалы.

Блоктар, ең алдымен, басқару ағыны үшін қолданылады, мысалы, if, while және циклдар үшін, ал бұл жағдайда блок ауқымы айнымалының ауқымы функцияның орындалу ағынының құрылымына байланысты болады дегенді білдіреді. Сонымен, блок ауқымы бар тілдер, әдетте, «жалаңаш» блоктарды қолдануға мүмкіндік береді, олардың мақсаты айнымалы ауқымды ұсақ бақылауға мүмкіндік беру болып табылады. Мысалы, көмекші айнымалыны блокта анықтауға болады, содан кейін оны қолдануға болады (айталық, функциясы бар айнымалыға қосылады) және блок аяқталғаннан кейін тастауға болады, немесе цикл ішінде пайдаланылатын айнымалыларды инициализациялайтын блокта біраз уақыт циклі болуы мүмкін тек бір рет инициализациялау керек.

Сияқты бірнеше бағдарламалау тілдерінің нәзіктігі Algol 68 және C (осы мысалда көрсетілген және содан бері стандартталған) C99 ), бұл блок ауқымының айнымалыларын тек блок денесінде ғана емес, егер бар болса, басқару операторының ішінде де жариялауға болады. Бұл функцияның декларациясында (функция денесінің блогы басталғанға дейін) және бүкіл функция денесінде қолданылатын функциялардың параметрлеріне ұқсас. Бұл бірінші кезекте қолданылады ілмектер үшін, цикл жағдайынан бөлек инициализация операторы бар, циклдан айырмашылығы және кең тараған идиома.

Блоктың масштабын көлеңке үшін қолдануға болады. Бұл мысалда блок ішінде көмекші айнымалы деп атауға болатын еді n, параметр атауын көлеңкелеу, бірақ бұл қате мүмкіндігіне байланысты нашар стиль болып саналады. Сонымен қатар, Java және C # сияқты C-дің кейбір ұрпақтары, блок ауқымын қолдауға ие болғанына қарамастан (жергілікті айнымалыны функция аяқталғанға дейін контексттен шығуға болады), бір жергілікті айнымалының екіншісін жасыруына жол бермейді. . Мұндай тілдерде екіншісін жариялауға тырысты n нәтижесінде синтаксистік қате пайда болады және оның бірі n айнымалылардың атын өзгерту керек болар еді.

Егер блок айнымалының мәнін орнату үшін қолданылса, блок ауқымы айнымалыны блоктан тыс жариялауды талап етеді. Бұл шартты операторларды қолдануды қиындатады жалғыз тапсырма. Мысалы, блок ауқымын пайдаланбайтын Python-да айнымалыны инициализациялауға болады:

егер c:    а = «ақымақ»басқа:    а = ""

қайда а кейін қол жетімді егер мәлімдеме.

Блоктың ауқымы бар Perl-де бұған блоктың алдында айнымалыны жариялау қажет:

менің $ a;егер (c) {    $ a = «ақымақ»;} басқа {    $ a = '';}

Көбіне бұл бірнеше тағайындау арқылы қайта жазылады, айнымалыны әдепкі мәнге дейін инициализациялайды. Python-да (қажет емес жерде) бұл:

а = ""егер c:    а = «ақымақ»

Перлде бұл:

менің $ a = '';егер (c) {    $ a = «ақымақ»;}

Бір айнымалы тағайындау жағдайында баламалы үштік оператор блокты болдырмау үшін, бірақ бұл көбіне бірнеше айнымалы тағайындау үшін мүмкін емес және күрделі логика үшін оқу қиын.

Бұл С-тегі маңызды мәселе, атап айтқанда, жолды тағайындау үшін, өйткені жолды инициализациялау автоматты түрде жадыны бөле алады, ал жолды тағайындау үшін қазірдің өзінде инициализацияланған айнымалыға жадыны, жолдың көшірмесін бөлу және олардың ойдағыдай орындалуын тексеру қажет.

қосалқы өсу_санағы {    менің $ counter = 0;    қайту қосалқы    {        қайту ++$ counter;    }}

Кейбір тілдер блоктың ауқымы тұжырымдамасын функциялардан тыс, әртүрлі көлемде қолдануға мүмкіндік береді. Мысалы, оң жақтағы Perl үзіндісінде, $ counter - бұл блоктың ауқымы бар айнымалы атауы (. қолдануына байланысты менің кілт сөз), while өсу_санағы - бұл ғаламдық ауқымға ие функция атауы. Әр қоңырау өсу_санағы мәнін арттырады $ counter бір мәнін беріп, жаңа мәнді қайтарыңыз. Осы блоктан тыс код қоңырау шала алады өсу_санағы, бірақ мәнін басқаша ала алмайды немесе өзгерте алмайды $ counter. Бұл идиома анықтауға мүмкіндік береді жабылу Перлде.

Функция ауқымы

Аттың қолданылу саласы ретінде белгілі функция болып табылады функция ауқымы. Функция ауқымы а құру әдісін ұсынатын көптеген бағдарламалау тілдерінде қол жетімді жергілікті айнымалы функцияда немесе ішкі программа: функция қайтарылған кезде аясы аяқталатын (контексттен тыс) айнымалы. Көп жағдайда айнымалының қызмет ету мерзімі функционалдық шақырудың ұзақтығы болып табылады - бұл an автоматты айнымалы, функция басталған кезде жасалады (немесе айнымалы жарияланады), функция қайтып оралғанда жойылады, ал айнымалының ауқымы функцияның шеңберінде болады, дегенмен «ішінде» мағынасы ауқым лексикалық немесе динамикалық болуына байланысты. Алайда, кейбір тілдер, мысалы, С, қарастырады статикалық жергілікті айнымалылар, мұндағы айнымалының өмір сүру уақыты - бағдарламаның барлық өмір сүру уақыты, бірақ айнымалы функцияның ішінде болған кезде ғана контекстте болады. Статикалық локальды айнымалылар жағдайында айнымалылар бағдарлама инициализацияланған кезде жасалады және тек бағдарлама аяқталған кезде жойылады, сияқты статикалық глобалды айнымалы, бірақ тек автоматты локальды айнымалы сияқты функцияның ішіндегі контекстте болады.

Маңыздысы, лексикалық ауқымда функционалдық ауқымы бар айнымалының тек шеңбер шеңбері болады лексикалық контекст функцияның функциясы: ол функция басқа функция шақырылған кезде контекстен шығады және функция қайтып оралғанда контекстке қайта оралады - аталған функциялар шақыру функциясының жергілікті айнымалыларына қол жеткізе алмайды, ал жергілікті айнымалылар тек контекстте болады олар жарияланған функцияның денесі. Керісінше, динамикалық ауқымда ауқым кеңейтіледі орындау мазмұны функциясы: жергілікті айнымалылар контекстте болу басқа функция шақырылған кезде, тек анықтайтын функция аяқталған кезде ғана контекстен шығады, сондықтан жергілікті айнымалылар олар анықталған функцияның контекстінде болады және барлық аталған функциялар. Лексикалық ауқымы бар тілдерде және кірістірілген функциялар, жергілікті айнымалылар кірістірілген функциялар үшін контекстте, өйткені олар сол лексикалық контекстте, бірақ лексикалық кірістірілген басқа функциялар үшін емес. Қоршау функциясының жергілікті айнымалысы а деп аталады жергілікті емес айнымалы кірістірілген функция үшін. Функцияның ауқымы да қолданылады жасырын функциялар.

деф шаршы(n):    қайту n * nдеф квадраттардың қосындысы(n):    барлығы = 0     мен = 0    уақыт мен <= n:        барлығы += шаршы(мен)        мен += 1    қайту барлығы

Мысалы, оң жақтағы Python кодының үзіндісінде екі функция анықталған: шаршы және квадраттардың қосындысы. шаршы санның квадратын есептейді; квадраттардың қосындысы барлық квадраттардың қосындысын санға дейін есептейді. (Мысалға, шаршы (4) 4.2 = 16, және квадраттардың қосындысы (4) 02 + 12 + 22 + 32 + 42 = 30.)

Осы функциялардың әрқайсысында аталған айнымалы болады n функцияның аргументін көрсететін. Бұл екеуі n айнымалылар бірдей атқа ие болғанымен, мүлдем бөлек және өзара байланысты емес, өйткені олар функционалды ауқымы бар лексикалық ауқымдағы жергілікті айнымалылар: әрқайсысының ауқымы өзіндік, лексикалық жағынан бөлек функция, сондықтан олар бір-біріне сәйкес келмейді. Сондықтан, квадраттардың қосындысы қоңырау шала алады шаршы өздікі жоқ n өзгертілуде. Сол сияқты, квадраттардың қосындысы атты айнымалылар бар барлығы және мен; бұл айнымалылар ауқымы шектеулі болғандықтан, аталған кез келген айнымалыларға кедергі болмайды барлығы немесе мен кез келген басқа функцияға тиесілі болуы мүмкін. Басқаша айтқанда, а қаупі жоқ аты соқтығысу осы атаулар мен кез-келген байланысты емес атаулар арасында, тіпті егер олар бірдей болса.

Ешқандай атау жасырылмайды: тек бір ғана айнымалы n кез-келген уақытта контексте болады, өйткені ауқымдары сәйкес келмейді. Керісінше, динамикалық ауқымы бар тілде жазуға ұқсас фрагмент болды n шақыру функциясында шақырылған функцияда контексте қалады, оның ауқымдары қабаттасады және жаңаға жасырылады («көлеңкеленген») n шақырылған функцияда.

Функциялар ауқымы едәуір күрделі, егер функциялар бірінші кластың объектілері болып табылса және оларды функцияға жергілікті түрде құруға болады, содан кейін қайтарылады. Бұл жағдайда кірістірілген функциядағы жергілікті емес кез-келген айнымалылар (функцияның анықталуындағы шектелмеген айнымалылар, олар контекстегі айнымалыларға шешіледі) жабу, тек функцияның өзі ғана емес, оның контекстін де (айнымалыларды) қайтару керек, содан кейін басқа контекстте ықтимал деп атауға болады. Бұл компилятордан едәуір көбірек қолдауды қажет етеді және бағдарламалық талдауды қиындата алады.

Файл ауқымы

Атауы - файл ретінде белгілі, ол белгілі файл ауқымы. Файлдың ауқымы көбінесе C (және C ++) үшін ерекше, мұнда файлдың жоғарғы деңгейінде (кез-келген функция шеңберінде емес) жарияланған айнымалылар мен функциялардың ауқымы бүкіл файлға, дәлірек айтсақ, C үшін, декларациядан бастап аяқталғанға дейін бастапқы файл, дәлірек айтсақ аударма бірлігі (ішкі байланыстыру). Мұны модульдер файлдармен сәйкестендірілетін модуль ауқымының формасы ретінде қарастыруға болады, ал қазіргі заманғы тілдерде айқын модуль шеңберімен ауыстырылады. Ішкі контекстке айнымалылар мен функциялар қосатын және одан әрі операторлар деп атай алатын операторлардың болуына байланысты, файл денесінде контексте не бар екенін анықтау қиынға соғады.

Жоғарыдағы С кодының үзіндісінде функция атауы квадраттардың қосындысы файл ауқымы бар.

Модуль ауқымы

Атаудың ауқымы модуль болып табылады, ол белгілі модуль ауқымы. Модуль ауқымы мына жерде орналасқан бағдарламалық модульдік тілдер мұнда (әр түрлі файлдарды қамтуы мүмкін) күрделі бағдарламаның негізгі бірлігі, өйткені олар ақпаратты жасыруға және шектеулі интерфейсті ашуға мүмкіндік береді. Модульдің ауқымы алғашқы болды Модула тілдер отбасы, және Python (оған Модула әсер еткен) - қазіргі заманғы үлгі.

Кейбіреулерінде объектіге бағытталған бағдарламалау модульдер үшін тікелей қолдау жоқ тілдер, мысалы, C ++, ұқсас құрылым класс иерархиясымен қамтамасыз етіледі, мұнда класстар бағдарламаның негізгі бірлігі болып табылады және сынып жеке әдістерге ие бола алады. Бұл контексте дұрыс түсінікті динамикалық диспетчер атаудың шешімі мен ауқымынан гөрі, олар көбінесе ұқсас рөлдерді ойнайды. Кейбір жағдайларда бұл мүмкіндіктер де бар, мысалы, модульдері де, кластары да бар Python-да, кодты ұйымдастыру (модуль деңгейіндегі функция немесе шартты түрде жеке әдіс ретінде) бағдарламашының таңдауы болып табылады.

Ғаламдық ауқым

Атаудың аясы - бұл белгілі бір бүтін бағдарлама ғаламдық ауқым. Ғаламдық ауқымы бар айнымалы атаулар - деп аталады жаһандық айнымалылар - аты-жөні соқтығысу және кездейсоқ маскировка мүмкіндігіне байланысты кейбір тілдерде жиі кездесетін жаман практика, сондай-ақ нашар модульділік, функция ауқымы немесе блок ауқымы қолайлы болып саналады. Алайда, ғаламдық ауқым әдетте (тілге байланысты) әр түрлі басқа атаулар үшін қолданылады, мысалы функциялардың атаулары, сыныптар және басқаларының атаулары деректер түрлері. Мұндай жағдайларда тетіктер атаулар кеңістігі соқтығысуды болдырмау үшін қолданылады.

Лексикалық ауқым мен динамикалық ауқым

Жергілікті айнымалыларды - белгілі бір функция шеңберінде болатын, шектеулі қолданылу аясындағы айнымалы атауларын пайдалану екі бірдей аталған айнымалылар арасындағы атаудың соқтығысу қаупін болдырмауға көмектеседі. Алайда, бұл сұраққа жауап берудің екі түрлі тәсілі бар: функцияның «шегінде» болу нені білдіреді?

Жылы лексикалық аясы (немесе лексикалық ауқым; деп те аталады статикалық ауқым немесе статикалық ауқым), егер айнымалы атауының ауқымы белгілі бір функция болса, онда оның ауқымы функцияның анықтамасының бағдарламалық мәтіні болып табылады: бұл мәтінде айнымалының аты бар және ол айнымалының мәнімен байланысты, бірақ сол мәтіннің сыртында айнымалының аты жоқ. Керісінше, жылы динамикалық ауқым (немесе динамикалық ауқым), егер айнымалы атауының ауқымы белгілі бір функция болса, онда оның ауқымы функцияны орындайтын уақыт кезеңі болып табылады: функция жұмыс істеп тұрған кезде айнымалының аты болады және оның мәніне байланысты болады, бірақ функция қайтып келеді , айнымалы атауы жоқ. Бұл дегеніміз, егер функция f бөлек анықталған функцияны орындайды ж, содан кейін лексикалық шеңберде, функция ж жасайды емес қол жетімді fжергілікті айнымалылар ж мәтінінің ішінде жоқ f), динамикалық ауқымда болса, функция ж жасайды қол жетімді fжергілікті айнымалылар (бастап ж шақыру кезінде шақырылады f).

$ # bash language$ х=1$ функциясы ж() { жаңғырық $ x ; х=2 ; }$ функциясы f() { жергілікті х=3 ; ж ; }$ f # бұл 1 немесе 3 басып шығарады ма?3$ жаңғырық $ x # бұл 1 немесе 2 басып шығарады ма?1

Мысалы, оң жақтағы бағдарламаны қарастырайық. Бірінші жол, х=1, жаһандық айнымалы жасайды х және оны инициализациялайды 1. Екінші жол, функциясы ж() { жаңғырық $ x ; х=2 ; }, функцияны анықтайды ж ағымдағы мәнін басып шығарады («жаңғырық») х, содан кейін орнатады х дейін 2 (алдыңғы мәнді қайта жазу). Үшінші жол, функциясы f() { жергілікті х=3 ; ж ; } функцияны анықтайды f жергілікті айнымалыны жасайды х (бірдей атаулы глобалды айнымалы жасыру) және оны инициализациялайды 3, содан кейін қоңырау шалады ж. Төртінші жол, f, қоңыраулар f. Бесінші жол, жаңғырық $ x, ағымдағы мәнін басып шығарады х.

Сонымен, бұл бағдарлама нақты нені басып шығарады? Бұл ауқым ережелеріне байланысты. Егер бұл бағдарламаның тілі лексикалық ауқымды қолданатын болса, онда ж жаһандық айнымалыны басып шығарады және өзгертеді х (өйткені ж сыртында анықталады f), сондықтан бағдарлама басып шығарады 1 содан соң 2. Керісінше, егер бұл тіл динамикалық ауқымды қолданса, онда ж басып шығарады және өзгертеді fжергілікті айнымалы х (өйткені ж ішінен шақырылады f), сондықтан бағдарлама басып шығарады 3 содан соң 1. (Бұл қалай болса, бағдарламаның тілі солай болады Баш динамикалық ауқымды қолданатын; сондықтан бағдарлама басып шығарады 3 содан соң 1. Егер дәл сол код іске қосылған болса ksh93 лексикалық аясын қолданатын болса, нәтижелері басқаша болар еді.)

Лексикалық қолдану аясы

Бірге лексикалық аясы, атау әрқашан оның лексикалық контекстіне сілтеме жасайды. Бұл бағдарлама мәтінінің қасиеті және орындалу уақытына тәуелсіз жасалады шақыру стегі тілді енгізу арқылы. Бұл сәйкестік тек статикалық бағдарлама мәтінін талдауды қажет ететіндіктен, бұл ауқым түрі де аталады статикалық ауқым. Лексикалық қолдану аясы стандартты АЛГОЛ сияқты негізді тілдер Паскаль, Модула-2 және Ада сияқты қазіргі заманғы функционалды тілдерде ML және Хаскелл. Ол сонымен қатар C тілі және оның синтаксистік және семантикалық туыстары, шектеулері әр түрлі болғанымен. Статикалық ауқым бағдарламалаушыға қарапайым атауды ауыстыру ретінде параметрлер, айнымалылар, тұрақтылар, типтер, функциялар және т.б. сияқты объектілік сілтемелер туралы ой қозғауға мүмкіндік береді. Бұл модульдік кодты жасауды жеңілдетеді және бұл туралы ой қозғау мүмкін, өйткені жергілікті атау құрылымын оқшау түсінуге болады. Керісінше, динамикалық ауқым бағдарламашыны модульдің коды шақырылуы мүмкін барлық орындалу жағдайларын күтуге мәжбүр етеді.

бағдарлама A;var Мен:бүтін;    Қ:char;    рәсім B;    var Қ:нақты;        L:бүтін;        рәсім C;        var М:нақты;        баста         (* A + B + C * ауқымы)        Соңы;     (* A + B * ауқымы)    Соңы; (* ауқым A *)Соңы.

Мысалы, Паскаль лексикалық ауқымға ие. Оң жағында Паскаль бағдарламасының фрагментін қарастырайық. Айнымалы Мен барлық нүктелерінде көрінеді, өйткені оны ешқашан аттас басқа айнымалы жасырмайды. The char айнымалы Қ арқылы жасырылғандықтан тек негізгі бағдарламада көрінеді нақты айнымалы Қ процедурада көрінеді B және C тек. Айнымалы L тек процедурада ғана көрінеді B және C бірақ ол басқа айнымалыны жасырмайды. Айнымалы М тек процедурада көрінеді C сондықтан рәсімнен де қол жетімді емес B немесе негізгі бағдарлама. Сонымен қатар, рәсім C тек процедурада көрінеді B және сондықтан оны негізгі бағдарламадан шақыруға болмайды.

Басқа процедура болуы мүмкін еді C бағдарламадан тыс мәлімделген B. Бағдарламадағы орны «C«деп аталады, содан кейін аталған екі процедураның қайсысы анықталады C ол айнымалылардың ауқымымен дәл осылай көрінеді.

Тілдеріндегі лексикалық ауқымын дұрыс жүзеге асыру бірінші класс кірістірілген функциялар тривиальды емес, өйткені ол әр функция мәніне айнымалылардың өзіне тәуелді болатын мәндердің жазбасын алып жүруді қажет етеді (функцияның жұбы және осы контекст а деп аталады жабу ). Жүзеге асырылуына байланысты және компьютерлік архитектура, айнымалы іздеу мүмкін сәл тиімсіз болып қалады[дәйексөз қажет ] лексикалық тұрғыдан өте терең болған кезде кірістірілген функциялар қолданылады, дегенмен мұны жеңілдетудің белгілі әдістері бар.[7][8] Сондай-ақ, тек өздерінің аргументтеріне және (бірден) жергілікті айнымалыларға сілтеме жасайтын функциялар үшін барлық қатысты орындарды мына жерден білуге ​​болады: жинақтау уақыты. Сондықтан кірістірілген функциялардың түрін пайдалану кезінде ешқандай қосымша шығындар болмайды. Дәл сол бағдарламаның кірістірілген функциялары қолданылмайтын белгілі бір бөліктеріне және, әрине, кірістірілген функциялары жоқ тілде жазылған бағдарламаларға қатысты (мысалы, Си тілінде).

Тарих

Императивті тіл үшін лексикалық ауқым қолданылды ALGOL 60 содан бері көптеген басқа императивті тілдерде алынған.[4]

Тілдер ұнайды Паскаль және C әрқашан лексикалық ауқымға ие болды, өйткені олардың екеуіне де енген идеялар әсер етеді ALGOL 60 және ALGOL 68 (дегенмен, С лексикалық құрамға кірмеген) кірістірілген функциялар ).

Перл кейіннен статикалық ауқымды қосқан динамикалық ауқымы бар тіл.

Түпнұсқа Лисп аудармашы (1960) динамикалық ауқымды қолданды. Терең байланыстырустатикалық (лексикалық) ауқымын жақындататын LISP 1.5-ке енгізілген Фунарг құрылғы Стив Рассел, астында жұмыс істейді Джон Маккарти ).

Барлығы ерте Лиспс динамикалық ауқымды, кем дегенде, аудармашыларға негізделген кезде қолданды. 1982 жылы кіші Гай Л. Стил және Common LISP Group жариялайды Жалпы LISP-ке шолу,[9] Лисптің осы уақытқа дейінгі тарихы мен әртүрлі іске асырылуына қысқаша шолу және оның ерекшеліктерін шолу Жалпы Лисп іске асыру керек. 102 бетте біз оқимыз:

LISP іске асыруларының көпшілігі іштей сәйкес келмейді, өйткені әдепкіде аудармашы мен компилятор бағдарламаларды түзету үшін әр түрлі семантикаларды тағайындай алады; бұл, ең алдымен, аудармашының барлық айнымалыларды динамикалық ауқымға теңестіруінен туындайды, ал компилятор басқа айнымалыларды локальды деп санайды. Бұл ыңғайлылық пен тиімділік үшін жасалды, бірақ өте нәзік қателерге әкелуі мүмкін. Жалпы LISP анықтамасы аудармашыдан және компилятордан дұрыс бағдарламаларға бірдей семантиканы енгізуді талап ету арқылы мұндай ауытқулардан аулақ болады.

Осылайша, жалпы LISP-ті енгізу қажет болды лексикалық аясы. Тағы да Жалпы LISP-ке шолу:

Сонымен қатар, Common LISP келесі мүмкіндіктерді ұсынады (олардың көпшілігі MacLisp, InterLisp немесе Lisp Machines Lisp-ден алынған): (...) Толық лексикалық ауқымды айнымалылар. «FUNARG проблемасы» деп аталатын[10][11] is completely solved, in both the downward and upward cases.

By the same year in which An overview of Common LISP was published (1982), initial designs (also by Guy L. Steele Jr.) of a compiled, lexically scoped Lisp, called Схема had been published and compiler implementations were being attempted. At that time, lexical scope in Lisp was commonly feared to be inefficient to implement. Жылы A History of T,[12] Olin Shivers writes:

Барлық serious Lisps in production use at that time were dynamically scoped. No one who hadn't carefully read the Rabbit[13] thesis (written by Guy Lewis Steele Jr. in 1978) believed lexical scope would fly; even the few people who болған read it were taking a bit of a leap of faith that this was going to work in serious production use.

The term "lexical scope" dates at least to 1967,[14] while the term "lexical scoping" dates at least to 1970, where it was used in Project MAC to describe the scope rules of the Lisp dialect MDL (then known as "Muddle").[15]

Dynamic scope

Бірге динамикалық ауқым, a name refers to execution context. It is uncommon in modern languages.[4] In technical terms, this means that each name has a global стек of bindings. Introducing a local variable with name х pushes a binding onto the global х stack (which may have been empty), which is popped off when the басқару ағыны leaves the scope. Бағалау х in any context always yields the top binding. Note that this cannot be done at compile-time because the binding stack only exists at жұмыс уақыты, which is why this type of scope is called динамикалық scope.

Generally, certain блоктар are defined to create bindings whose lifetime is the execution time of the block; this adds some features of static scope to the dynamic scope process. However, since a section of code can be called from many different locations and situations, it can be difficult to determine at the outset what bindings will apply when a variable is used (or if one exists at all). This can be beneficial; application of the principle of least knowledge suggests that code avoid depending on the себептері for (or circumstances of) a variable's value, but simply use the value according to the variable's definition. This narrow interpretation of shared data can provide a very flexible system for adapting the behavior of a function to the current state (or policy) of the system. However, this benefit relies on careful documentation of all variables used this way as well as on careful avoidance of assumptions about a variable's behavior, and does not provide any mechanism to detect interference between different parts of a program. Some languages, like Перл және Жалпы Лисп, allow the programmer to choose static or dynamic scope when defining or redefining a variable. Examples of languages that use dynamic scope include Логотип, Эмакс Лисп, LaTeX and the shell languages bash, сызықша, және PowerShell.

Dynamic scope is fairly easy to implement. To find an name's value, the program could traverse the runtime stack, checking each activation record (each function's stack frame) for a value for the name. In practice, this is made more efficient via the use of an association list, which is a stack of name/value pairs. Pairs are pushed onto this stack whenever declarations are made, and popped whenever variables go out of context.[16] Shallow binding is an alternative strategy that is considerably faster, making use of a central reference table, which associates each name with its own stack of meanings. This avoids a linear search during run-time to find a particular name, but care should be taken to properly maintain this table.[16] Note that both of these strategies assume a last-in-first-out (ЛИФО ) ordering to bindings for any one variable; in practice all bindings are so ordered.

An even simpler implementation is the representation of dynamic variables with simple global variables. The local binding is performed by saving the original value in an anonymous location on the stack that is invisible to the program. When that binding scope terminates, the original value is restored from this location. In fact, dynamic scope originated in this manner. Early implementations of Lisp used this obvious strategy for implementing local variables, and the practice survives in some dialects which are still in use, such as GNU Emacs Lisp. Lexical scope was introduced into Lisp later. This is equivalent to the above shallow binding scheme, except that the central reference table is simply the global variable binding context, in which the current meaning of the variable is its global value. Maintaining global variables isn't complex. For instance, a symbol object can have a dedicated slot for its global value.

Dynamic scope provides an excellent abstraction for thread local storage, but if it is used that way it cannot be based on saving and restoring a global variable. A possible implementation strategy is for each variable to have a thread-local key. When the variable is accessed, the thread-local key is used to access the thread-local memory location (by code generated by the compiler, which knows which variables are dynamic and which are lexical). If the thread-local key does not exist for the calling thread, then the global location is used. When a variable is locally bound, the prior value is stored in a hidden location on the stack. The thread-local storage is created under the variable's key, and the new value is stored there. Further nested overrides of the variable within that thread simply save and restore this thread-local location. When the initial, outermost override's context terminates, the thread-local key is deleted, exposing the global version of the variable once again to that thread.

Бірге referential transparency the dynamic scope is restricted to the argument stack of the current function only, and coincides with the lexical scope.

Macro expansion

In modern languages, macro expansion ішінде алдын ала процессор is a key example of de facto dynamic scope. The macro language itself only transforms the source code, without resolving names, but since the expansion is done in place, when the names in the expanded text are then resolved (notably free variables), they are resolved based on where they are expanded (loosely "called"), as if dynamic scope were occurring.

The C алдын ала процессоры үшін қолданылады macro expansion, has de facto dynamic scope, as it does not do name resolution by itself. For example, the macro:

#define ADD_A(x) x + a

will expand to add а to the passed variable, with this name only later resolved by the compiler based on where the macro ADD_A is "called" (properly, expanded), is in dynamic scope, and is independent of where the macro is defined. Properly, the C preprocessor only does lexical analysis, expanding the macro during the tokenization stage, but not parsing into a syntax tree or doing name resolution.

For example, in the following code, the а in the macro is resolved (after expansion) to the local variable at the expansion site:

#define ADD_A(x) x + aжарамсыз add_one(int *х) {  const int а = 1;  *х = ADD_A(*х);}жарамсыз add_two(int *х) {  const int а = 2;  *х = ADD_A(*х);}

Qualified names

As we have seen, one of the key reasons for scope is that it helps prevent name collisions, by allowing identical names to refer to distinct things, with the restriction that the names must have separate scopes. Sometimes this restriction is inconvenient; when many different things need to be accessible throughout a program, they generally all need names with global scope, so different techniques are required to avoid name collisions.

To address this, many languages offer mechanisms for organizing global names. The details of these mechanisms, and the terms used, depend on the language; but the general idea is that a group of names can itself be given a name — a prefix — and, when necessary, an entity can be referred to by a qualified name consisting of the name plus the prefix. Normally such names will have, in a sense, two sets of scopes: a scope (usually the global scope) in which the qualified name is visible, and one or more narrower scopes in which the unqualified name (without the prefix) is visible as well. And normally these groups can themselves be organized into groups; that is, they can be кірістірілген.

Although many languages support this concept, the details vary greatly. Some languages have mechanisms, such as атаулар кеңістігі жылы C ++ және C #, that serve almost exclusively to enable global names to be organized into groups. Other languages have mechanisms, such as пакеттер жылы Ада және құрылымдар жылы Стандартты ML, that combine this with the additional purpose of allowing some names to be visible only to other members of their group. And object-oriented languages often allow classes or singleton objects to fulfill this purpose (whether or not they сонымен қатар have a mechanism for which this is the primary purpose). Furthermore, languages often meld these approaches; Мысалға, Перл 's packages are largely similar to C++'s namespaces, but optionally double as classes for object-oriented programming; және Java organizes its variables and functions into classes, but then organizes those classes into Ada-like packages.

Тіл бойынша

Scope rules for representative languages follow.

C

In C, scope is traditionally known as байланыстыру немесе көріну, particularly for variables. C is a lexically scoped language with global scope (known as external linkage), a form of module scope or file scope (known as internal linkage), and local scope (within a function); within a function scopes can further be nested via block scope. However, standard C does not support nested functions.

The lifetime and visibility of a variable are determined by its storage class. There are three types of lifetimes in C: static (program execution), automatic (block execution, allocated on the stack), and manual (allocated on the heap). Only static and automatic are supported for variables and handled by the compiler, while manually allocated memory must be tracked manually across different variables. There are three levels of visibility in C: external linkage (global), internal linkage (roughly file), and block scope (which includes functions); block scopes can be nested, and different levels of internal linkage is possible by use of includes. Internal linkage in C is visibility at the translation unit level, namely a source file after being processed by the C алдын ала процессоры, notably including all relevant includes.

C programs are compiled as separate нысан файлдары, which are then linked into an executable or library via a байланыстырушы. Thus name resolution is split across the compiler, which resolves names within a translation unit (more loosely, "compilation unit", but this is properly a different concept), and the linker, which resolves names across translation units; қараңыз байланыстыру for further discussion.

In C, variables with block scope enter context when they are declared (not at the top of the block), go out of context if any (non-nested) function is called within the block, come back into context when the function returns, and go out of context at the end of the block. In the case of automatic local variables, they are also allocated on declaration and deallocated at the end of the block, while for static local variables, they are allocated at program initialization and deallocated at program termination.

The following program demonstrates a variable with block scope coming into context partway through the block, then exiting context (and in fact being deallocated) when the block ends:

# қосу <stdio.h>int негізгі(жарамсыз) {  char х = 'm';  printf("%c n", х);  {    printf("%c n", х);    char х = 'b';    printf("%c n", х);  }  printf("%c n", х);}

The program outputs:

mmbm

There are other levels of scope in C.[17] Variable names used in a function prototype have function prototype visibility, and exit context at the end of the function prototype. Since the name is not used, this is not useful for compilation, but may be useful for documentation. Label names for GOTO statement have function scope, while case label names for switch statements have block scope (the block of the switch).

C ++

All the variables that we intend to use in a program must have been declared with its type specifier in an earlierpoint in the code, like we did in the previous code at the beginning of the body of the function main when wedeclared that a, b, and result were of type int.A variable can be either of global or local scope. A global variable is a variable declared in the main body of thesource code, outside all functions, while a local variable is one declared within the body of a function or a block.

Қазіргі нұсқалары рұқсат ету nested lexical scope.

Свифт

Swift has a similar rule for scopes with C++, but contains different access modifiers.

ModifierImmediate scopeФайлContaining module/packageӘлемнің қалған бөлігі
ашықИәИәИәYes, allows subclass
қоғамдықИәИәИәYes, disallows subclass
ішкіИәИәИәЖоқ
fileprivateИәИәЖоқЖоқ
жекеИәЖоқЖоқЖоқ

Барыңыз

Барыңыз is lexically scoped using blocks.[3]

Java

Java is lexically scoped.

A Java class can contain three types of variables:[18]

Local variables
are defined inside a method, or a particular block. These variables are local to where they were defined and lower levels. For example, a loop inside a method can use that method's local variables, but not the other way around. The loop's variables (local to that loop) are destroyed as soon as the loop ends.
Member variables
деп те аталады өрістер are variables declared within the class, outside of any method. By default, these variables are available for all methods within that class and also for all classes in the package.
Параметрлер
are variables in method declarations.

In general, a set of brackets defines a particular scope, but variables at top level within a class can differ in their behavior depending on the modifier keywords used in their definition.The following table shows the access to members permitted by each modifier.[19]

ModifierСыныпПакетІшкі сыныпӘлем
қоғамдықИәИәИәИә
қорғалғанИәИәИәЖоқ
(no modifier)ИәИәЖоқЖоқ
жекеИәЖоқЖоқЖоқ

JavaScript

JavaScript has simple scope rules,[20] but variable initialization and name resolution rules can cause problems, and the widespread use of closures for callbacks means the lexical context of a function when defined (which is used for name resolution) can be very different from the lexical context when it is called (which is irrelevant for name resolution). JavaScript objects have name resolution for properties, but this is a separate topic.

JavaScript has lexical scope [21] nested at the function level, with the global context being the outermost context. This scope is used for both variables and for functions (meaning function declarations, as opposed to variables of function type).[22] Block scope with the рұқсат етіңіз және const keywords is standard since ECMAScript 6. Block scope can be produced by wrapping the entire block in a function and then executing it; бұл белгілі immediately-invoked function expression (IIFE) pattern.

While JavaScript scope is simple—lexical, function-level—the associated initialization and name resolution rules are a cause of confusion. Firstly, assignment to a name not in scope defaults to creating a new global variable, not a local one. Secondly, to create a new local variable one must use the var keyword; the variable is then created at the top of the function, with value белгісіз and the variable is assigned its value when the assignment expression is reached:

A variable with an Initialiser is assigned the value of its AssignmentExpression қашан VariableStatement is executed, not when the variable is created.[23]

Бұл белгілі variable hoisting[24]—the declaration, but not the initialization, is hoisted to the top of the function. Thirdly, accessing variables before initialization yields белгісіз, rather than a syntax error. Fourthly, for function declarations, the declaration and the initialization are both hoisted to the top of the function, unlike for variable initialization. For example, the following code produces a dialog with output белгісіз, as the local variable declaration is hoisted, shadowing the global variable, but the initialization is not, so the variable is undefined when used:

а = 1;функциясы f() {  ескерту(а);  var а = 2;}f();

Further, as functions are first-class objects in JavaScript and are frequently assigned as callbacks or returned from functions, when a function is executed, the name resolution depends on where it was originally defined (the lexical context of the definition), not the lexical context or execution context where it is called. The nested scopes of a particular function (from most global to most local) in JavaScript, particularly of a closure, used as a callback, are sometimes referred to as the scope chain, by analogy with the prototype chain of an object.

Жабықтар can be produced in JavaScript by using nested functions, as functions are first-class objects.[25] Returning a nested function from an enclosing function includes the local variables of the enclosing function as the (non-local) lexical context of the returned function, yielding a closure. Мысалға:

функциясы newCounter() {  // return a counter that is incremented on call (starting at 0)  // and which returns its new value  var а = 0;  var б = функциясы() { а++; қайту а; };  қайту б;}c = newCounter();ескерту(c() + ' ' + c());  // outputs "1 2"

Closures are frequently used in JavaScript, due to being used for callbacks. Indeed, any hooking of a function in the local context as a callback or returning it from a function creates a closure if there are any unbound variables in the function body (with the context of the closure based on the nested scopes of the current lexical context, or "scope chain"); this may be accidental. When creating a callback based on parameters, the parameters must be stored in a closure, otherwise it will accidentally create a closure that refers to the variables in the enclosing context, which may change.[26]

Name resolution of properties of JavaScript objects is based on inheritance in the prototype tree—a path to the root in the tree is called a prototype chain—and is separate from name resolution of variables and functions.

Лисп

Лисп dialects have various rules for scope.

The original Lisp used dynamic scope; ол болды Схема, шабыт АЛГОЛ, that introduced static (lexical) scope to the Lisp family.

Maclisp used dynamic scope by default in the interpreter and lexical scope by default in compiled code, though compiled code could access dynamic bindings by use of SPECIAL declarations for particular variables.[27] Алайда, Maclisp treated lexical binding more as an optimization than one would expect in modern languages, and it did not come with the жабу feature one might expect of lexical scope in modern Lisps. A separate operation, *FUNCTION, was available to somewhat clumsily work around some of that issue.[28]

Жалпы Лисп adopted lexical scope from Схема,[29] сияқты Clojure.

ИСЛИСП has lexical scope for ordinary variables. It also has dynamic variables, but they are in all cases explicitly marked; they must be defined by a defdynamic special form, bound by a dynamic-let special form, and accessed by an explicit динамикалық special form.[30]

Some other dialects of Lisp, like Эмакс Лисп, still use dynamic scope by default. Emacs Lisp now has lexical scope available on a per-buffer basis.[31]

Python

For variables, Python has function scope, module scope, and global scope. Names enter context at the start of a scope (function, module, or global scope), and exit context when a non-nested function is called or the scope ends. If a name is used prior to variable initialization, this raises a runtime exception. If a variable is simply accessed (not assigned to), name resolution follows the LEGB (Local, Enclosing, Global, Built-in) rule which resolves names to the narrowest relevant context. However, if a variable is assigned to, it defaults to declaring a variable whose scope starts at the start of the level (function, module, or global), not at the assignment. Both these rules can be overridden with a ғаламдық немесе жергілікті емес (in Python 3) declaration prior to use, which allows accessing global variables even if there is a masking nonlocal variable, and assigning to global or nonlocal variables.

As a simple example, a function resolves a variable to the global scope:

>>> деф f():...     басып шығару(х)...>>> х = "global">>> f()ғаламдық

Ескертіп қой х is defined before f is called, so no error is raised, even though it is defined after its reference in the definition of f. Lexically this is a forward reference, which is allowed in Python.

Here assignment creates a new local variable, which does not change the value of the global variable:

>>> деф f():...     х = "f"...     басып шығару(х)...>>> х = "global">>> басып шығару(х)ғаламдық>>> f()f>>> басып шығару(х)ғаламдық

Assignment to a variable within a function causes it to be declared local to the function, hence its scope is the entire function, and thus using it prior to this assignment raises an error. This differs from C, where the scope of the local variable start at its declaration. This code raises an error:

>>> деф f():...     басып шығару(х)...     х = "f"...>>> х = "global">>> f()Traceback (most recent call last):  Файл "", line 1, жылы <module>  Файл "", line 2, жылы fUnboundLocalError: local variable 'x' referenced before assignment

The default name resolution rules can be overridden with the ғаламдық немесе жергілікті емес (in Python 3) keywords. In the below code, the global x declaration in ж дегенді білдіреді х resolves to the global variable. It thus can be accessed (as it has already been defined), and assignment assigns to the global variable, rather than declaring a new local variable. Note that no ғаламдық declaration is needed in f—since it does not assign to the variable, it defaults to resolving to the global variable.

>>> деф f():...     басып шығару(х)...>>> деф ж():...     ғаламдық х...     басып шығару(х)...     х = "g"...>>> х = "global">>> f()ғаламдық>>> ж()ғаламдық>>> f()ж

ғаламдық can also be used for nested functions. In addition to allowing assignment to a global variable, as in an unnested function, this can also be used to access the global variable in the presence of a nonlocal variable:

>>> деф f():...     деф ж():...         ғаламдық х...         басып шығару(х)...     х = "f"...     ж()...>>> х = "global">>> f()ғаламдық

For nested functions, there is also the жергілікті емес declaration, for assigning to a nonlocal variable, similar to using ғаламдық in an unnested function:

>>> деф f():...     деф ж():...         жергілікті емес х  # Python 3 only...         х = "g"...     х = "f"...     ж()...     басып шығару(х)...>>> х = "global">>> f()ж>>> басып шығару(х)ғаламдық

R

R is a lexically scoped language, unlike other implementations of S where the values of free variables are determined by a set of global variables, while in R they are determined by the context in which the function was created.[32] The scope contexts may be accessed using a variety of features (such as parent.frame()) which can simulate the experience of dynamic scope should the programmer desire.

There is no block scope:

а <- 1{  а <- 2}хабар(а)## 2

Functions have access to scope they were created in:

а <- 1f <- функциясы() {  хабар(а)}f()## 1

Variables created or modified within a function stay there:

а <- 1f <- функциясы() {  хабар(а)  а <- 2  хабар(а)}f()## 1## 2хабар(а)## 1

Variables created or modified within a function stay there unless assignment to enclosing scope is explicitly requested:

а <- 1f <- функциясы() {  хабар(а)  а <<- 2  хабар(а)}f()## 1## 2хабар(а)## 2

Although R has lexical scope by default, function scopes can be changed:

а <- 1f <- функциясы() {  хабар(а)}my_env <- new.env()my_env$а <- 2f()## 1қоршаған орта(f) <- my_envf()## 2

Сондай-ақ қараңыз

Ескертулер

  1. ^ Қараңыз анықтама for meaning of "scope" versus "context".
  2. ^ "Dynamic scope" bases name resolution on дәрежесі (lifetime), not ауқымы, and thus is formally inaccurate.
  3. ^ Мысалы, Джинджа template engine for Python by default uses both lexical scope (for imports) and dynamic scope (for includes), and allows behavior to be specified with keywords; қараңыз Import Context Behavior.
  4. ^ "Name resolution" and "name binding" are largely synonymous; narrowly speaking "resolution" determines to which name a particular use of a name refers, without associating it with any meaning, as in higher-order abstract syntax, while "binding" associates the name with an actual meaning. In practice the terms are used interchangeably.
  5. ^ Үшін өзін-өзі өзгертетін код the lexical context itself can change during run time.
  6. ^ By contrast, *"a name binding's context", *"a name binding coming into scope" or *"a name binding going out of scope" are all incorrect—a name binding has scope, while a part of a program has context.

Әдебиеттер тізімі

  1. ^ "Report on the Algorithmic Language Algol 60", 2.7. Quantities, kinds and scopes
  2. ^ WG14 N1256 (2007 updated version of the C99 standard), 6.2.1 Scopes of identifiers, 2007-09-07
  3. ^ а б Go бағдарламалау тілінің спецификасы: Declarations and scope, Version of Nov 13, 2013
  4. ^ а б c Borning A. CSE 341 -- Lexical and Dynamic Scoping. University of Washington.
  5. ^ Crockford, Douglas. "Code Conventions for the JavaScript Programming Language". Алынған 2015-01-04.
  6. ^ Backus, J. W.; Вегштейн, Дж. Х .; Van Wijngaarden, A.; Woodger, M.; Bauer, F. L.; Жасыл, Дж .; Кац, С .; МакКарти, Дж .; Perlis, A. J.; Рутишаузер, Х .; Самелсон, К .; Vauquois, B. (1960). «ALGOL 60 алгоритмдік тілі туралы есеп». ACM байланысы. 3 (5): 299. дои:10.1145/367236.367262. S2CID  278290.
  7. ^ "Programming Language Pragmatics ", LeBlank-Cook symbol table
  8. ^ "A Symbol Table Abstraction to Implement Languages with Explicit Scope Control ", LeBlank-Cook, 1983
  9. ^ Louis Steele, Guy (August 1982). "An overview of Common LISP". LFP '82: Proceedings of the 1982 ACM Symposium on LISP and Functional Programming: 98–107. дои:10.1145/800068.802140. ISBN  0897910826. S2CID  14517358.
  10. ^ Joel, Moses (June 1970). "The Function of FUNCTION in LISP". MIT AI Memo 199. MIT Artificial Intelligence Lab.
  11. ^ Steele, Guy Lewis Jr.; Sussman, Gerald Jay (May 1978). "The Art of the Interpreter; or, The Modularity Complex (Parts Zero, One and Two)". MIT AI Memo 453. MIT Artificial Intelligence Lab.
  12. ^ Shivers, Olin. "History of T". Пол Грэм. Алынған 5 ақпан 2020.
  13. ^ Steele, Guy Lewis Jr. (May 1978). "RABBIT: A Compiler for SCHEME". MIT. hdl:1721.1/6913. Журналға сілтеме жасау қажет | журнал = (Көмектесіңдер)
  14. ^ "лексикалық аясы ", Computer and Program Organization, Part 3, б. 18, at Google Books, University of Michigan. Engineering Summer Conferences, 1967
  15. ^ "лексикалық ауқым ", Project MAC Progress Report, Volume 8, б. 80, at Google Books, 1970.
  16. ^ а б Скотт 2009, 3.4 Implementing Scope, p. 143.
  17. ^ "Қолдану аясы ", XL C/C++ V8.0 for Linux, IBM
  18. ^ "Declaring Member Variables (The Java™ Tutorials > Learning the Java Language > Classes and Objects)". docs.oracle.com. Алынған 19 наурыз 2018.
  19. ^ "Controlling Access to Members of a Class (The Java™ Tutorials > Learning the Java Language > Classes and Objects)". docs.oracle.com. Алынған 19 наурыз 2018.
  20. ^ "Everything you need to know about Javascript variable scope ", Saurab Parakh, Coding is Cool, 2010-02-08
  21. ^ "Annotated ES5". es5.github.io. Алынған 19 наурыз 2018.
  22. ^ «Функциялар». MDN веб-құжаттары. Алынған 19 наурыз 2018.
  23. ^ "12.2 Variable Statement ", Annotated ECMAScript 5.1, Last updated: 2012-05-28
  24. ^ "JavaScript Scoping and Hoisting ", Ben Cherry, Adequately Good, 2010-02-08
  25. ^ Javascript Closures, Richard Cornford. Наурыз 2004 ж
  26. ^ "Explaining JavaScript Scope And Closures ", Robert Nyman, October 9, 2008
  27. ^ Pitman, Kent (December 16, 2007). "The Revised Maclisp Manual (The Pitmanual), Sunday Morning Edition". MACLISP.info. HyperMeta Inc. Declarations and the Compiler, Concept "Variables". Алынған 20 қазан, 2018. If the variable to be bound has been declared to be special, the binding is compiled as code to imitate the way the interpreter binds variables
  28. ^ Pitman, Kent (December 16, 2007). "The Revised Maclisp Manual (The Pitmanual), Sunday Morning Edition". MACLISP.info. HyperMeta Inc. The Evaluator, Special Form *FUNCTION. Алынған 20 қазан, 2018. *FUNCTION is intended to help solve the “funarg problem,” however it only works in some easy cases.
  29. ^ Pitman, Kent; т.б. (webbed version of ANSI standard X3.226-1994) (1996). "Common Lisp HyperSpec". Lispworks.com. LispWorks Ltd. 1.1.2 History. Алынған 20 қазан, 2018. MacLisp improved on the Lisp 1.5 notion of special variables ... The primary influences on Common Lisp were Lisp Machine Lisp, MacLisp, NIL, S-1 Lisp, Spice Lisp, and Scheme.
  30. ^ "Programming Language ISLISP, ISLISP Working Draft 23.0" (PDF). ISLISP.info. 11.1 The lexical principle. Алынған 20 қазан, 2018. Dynamic bindings are established and accessed by a separate mechanism (i.e., defdynamic, dynamic-let, және динамикалық).
  31. ^ "Lexical Binding". EmacsWiki. Алынған 20 қазан, 2018. Emacs 24 has optional lexical binding, which can be enabled on a per-buffer basis.
  32. ^ "R FAQ". cran.r-project.org. Алынған 19 наурыз 2018.