Oblast vidljivosti

S Vikipedije, slobodne enciklopedije

U programiranju, oblast vidljivosti (eng. scope ) je deo programskog koda u kome je vezivanje imena (eng. name binding ) validno, pri čemu se ime može koristiti za upućivanje na entitet. Takva oblast naziva se scope block . U ostalim delovima koda ime se može odnositi na drugi entitet (može biti drugačije vezano) ili može biti nepovezano.

Oblast vezivanja je takođe poznata kao vidljivost entiteta, posebno u starijoj literaturi - iz perspektive navedenog entiteta, a ne referentnog naziva.

Oblast vidljivosti promenljive je deo programa koji je ili može biti oblast skupa vezivanja - teško je precizno definisati, ali u praksi u velikoj meri odgovara bloku, funkciji ili datoteci, zavisno od programskog jezika i tipa entiteta. Izraz "scope" koristi se i za upućivanje na skup svih vidljivih entiteta ili imena koja važe u delu programa ili u određenoj datoj tački programa, što se preciznije naziva context ili okruženje.

U praksi se za većinu programskih jezika „deo programa“ upućuje na „deo izvornog koda (područje teksta)“ i poznat je kao leksička oblast vidljivosti (lexical scope). Međutim, u nekim programskim jezicima se „deo programa“ odnosi na „deo vremena izvođenja (vreme izvršavanja koda)“ i poznat je kao dinamička oblast vidljivosti(dynamic scope).

U većini slučajeva name resolution zasnovana na leksičkoj oblasti vidljivosti relativno je jednostavna za upotrebu i za primenu, jer se tokom upotrebe može pročitati unazad u izvornom kodu da bi se utvrdilo na koji se entitet jedno ime odnosi, a u implementaciji se može održavati lista imena i konteksta prilikom kompajliranja ili interpretacije programa. Poteškoće nastaju pri maskiranju imena i hostovanju, dok znatno suptilnije nastaju kod nelokalnih promenljivih, posebno u zatvorenjima.

Definicija[uredi | uredi izvor]

Leksička oblast vidljivosti imena (identifikatora) je „deo izvornog koda na koji se odnosi vezivanje imena sa entitetom“ - i definicija je praktično nepromenjena u odnosu na istu iz 1960. godine po specifikaciji ALGOL 60. Slede specifikacije jezika ALGOL 60.

ALGOL 60 (1960)

Razlikuju se sledeće vrste veličina: jednostavne promenljive, nizovi, oznake, promene i procedure. Oblast vidljivosti veličine je skup iskaza i izraza u kojima je deklaracija identifikatora povezanog s tom veličinom validna.

C (2007)[1]

Identifikator može označavati objekat, funkciju, člana strukture, veze ili nabrajanja, typedef name, ime oznake, ime makro-a ili makro parametar. Isti identifikator može označavati različite entitete u različitim tačkama programa. Za svaki različiti entitet koji označi identifikator, identifikator je vidljiv (tj. može se koristiti) samo u području teksta programa koji se zove njegova oblast vidljivosti.

Go (2013)[2]

Deklaracija vezuje non-blank identifikator za konstantu, tip, promenljivu, funkciju ili paket. Oblast vidljivosti deklarisanog identifikatora je opseg izvornog teksta u kojem identifikator označava navedenu konstantu, tip, promenljivu, funkciju ili paket.

Najčešće se oblast vidljivosti odnosi na to kada se određeno ime može odnositi na datu promenljivu - kada deklaracija ima efekta - ali može da se primenjuje i na druge entitete, kao što su funkcije, tipovi, klase, oznake, konstante i nabrajanja.

Leksički opseg nasuprot dinamičkom opsegu[uredi | uredi izvor]

Osnovna razlika u opsegu obuhvata značenje „dela programa“. U jezicima s leksičkim opsegom (koji se nazivaju i statički opseg), rezolucija imena zavisi od lokacije u izvornom kodu i leksičkog konteksta, koja je definisana mestom definisane imenovane promenljive ili funkcije. Suprotno tome, u jezicima sa dinamičkim opsegom rezolucija imena zavisi od stanja programa u kome se pojavljuje ime koje je određeno kontekstom izvršavanja ili kontekstom poziva. U praksi, sa leksičkim opsegom definicija promenljive rešava se pretragom njenog bloka koji sadrži ili funkciju, zatim ako to ne uspe u pretraživanju spoljnog bloka i slično, dok se sa dinamičkim opsegom traži funkcija poziva, tada funkcija koja je pozvala taj poziv funkciju, i tako dalje, napredovanje na nizu poziva.[3] Naravno, u oba pravila prvo tražimo lokalnu definiciju promenljive.

Većina savremenih jezika koristi leksičko određivanje za promenljive i funkcije, mada se dinamički opseg koristi u nekim jezicima, naročito u nekim dijalektima Lisp-a, nekim „skriptnim“ jezicima i nekim jezicima šablona. Perl 5 nudi i leksički i dinamički opseg. Čak i u leksički obuhvaćenim jezicima, prostor za zatvorenje može biti zbunjujući, jer oni zavise od leksičkog konteksta gde je zatvorenje definisano, a ne gde se naziva.

Leksička rezolucija se može odrediti u toku prevođenja, a poznata je i kao rano vezivanje, dok se dinamička rezolucija u celini može odrediti samo tokom izvođenja, pa je stoga poznata i kao kasno vezivanje.

Srodni koncepti[uredi | uredi izvor]

U objektno orijentisanom programiranju dinamičko obaveštenje odabira objektnu metodu za vreme izvođenja, iako se stvarno vezivanje imena vrši u vreme prevođenja ili vreme izvođenja u zavisnosti od  jezika. De facto dinamički opseg je uobičajen u makro jezicima, koji ne omogućavaju direktno razrešavanje imena, već se umesto toga proširuju.

Neki programski okviri poput AngularJS koriste izraz „domet“ da bi značili nešto sasvim drugo od načina na koji se koristi u ovom članku. U tim je okvirima samo predmet programskog jezika koji koriste (JavaSkript u slučaju AngularJS) koji se na određeni način koristi okvirom za oponašanje dinamičkog opsega na jeziku koji za svoje promenljive koristi leksički opseg. Ti opsezi AngularJS mogu sami biti u dometu ili izvan njegovog dometa(koristeći uobičajeno značenje termina) u bilo kom delu programa, sledeći uobičajena pravila promenljivog opsega jezika kao i bilo koji drugi objekat i koristeći sopstveno nasleđivanje i pravila o isključivanju. U kontekstu AngularJS, ponekad se koristi termin „$opseg“ (sa znakom dolara) da se izbegne zabuna, ali upotreba znaka dolara u promenljivim imenima često obeshrabrujuće vodiče za stil.[4]

Upotreba [uredi | uredi izvor]

Opseg je važna komponenta name resolution,  koja je za uzvrat osnovna za jezičku semantiku. Rezolucija imena (uključujući opseg) varira između programskih jezika i unutar programskog jezika, ovisno o vrsti entiteta; pravila za obim se nazivaju pravila o opsegu ili pravila za obim. Zajedno sa imenima prostora, pravila za opseg su ključna za modularno programiranje, tako da promena u jednom delu programa ne prekida nepovezani deo.

Pregled[uredi | uredi izvor]

Kada se govori o dometu, postoje tri osnovna koncepta: obim, obim i kontekst. Konkretno „opseg“ i „kontekst“ često se mešaju: obim je osobina identifikatora i fiksan je, dok je kontekst osobina programa, koji se razlikuje zavisno od položaja. Tačnije, kontekst je osobina položaja u programu, bilo pozicije u izvornom kodu (leksički kontekst) ili tačke tokom vremena izvođenja (kontekst izvršenja, kontekst izvođenja ili kontekst poziva). Kontekst izvršenja sastoji se od leksičkog konteksta (u trenutnoj izvršnoj tački) plus dodatnog stanja izvršenja, kao što je stog poziva.  Dakle, kada se tačka izvršenja programa nalazi u opsegu imena promenljive, "promenljiva (ime) je u kontekst "(što znači" u kontekstu u ovoj tački "), a kada izvršna tačka" napušta opseg promenljive (ime) ", kao što je povratak iz funkcije," promenljiva (ime) izlazi iz konteksta " . Usko rečeno, tokom izvođenja program ulazi i izlazi iz različitih opsega, a na mestu izvršenja identifikatori su "u kontekstu" ili "nisu u kontekstu", dakle identifikatori "dolaze u kontekst" ili "izlaze iz konteksta" kako program ulazi ili izlazi iz opsega - međutim, u praksi je upotreba mnogo slabija. Opseg je koncept nivoa izvornog koda i svojstvo identifikatora, posebno promenljivih ili funkcija, funkcija - identifikatori u izvornom kodu su reference na entitete u programu - i deo je ponašanja prevodioca ili prevodioca jezika.

Kao takva, pitanja opsega slična su pokazivačima, koji su vrsta reference koja se generalno koristi u programima. Upotreba vrednosti promenljive kada je ime u kontekstu, ali promenljiva je neinicijalizovana i analogna je dereferenciranju (pristupu vrednosti) divljeg pokazivača, jer je nedefinisana. Međutim, kako se promenljive ne uništavaju sve dok ne izađu iz konteksta, analogni viseći pokazivač ne postoji.

Za entitete kao što su promenljive, opseg je podskup životnog veka (poznat i kao obim) - ime se može odnositi samo na promenljivu koja postoji (verovatno sa nedefinisanu vrednost), ali promenljive koje postoje nisu nužno vidljive: promenljiva može postojati, ali biti nepristupačna (vrednost se čuva, ali se ne odnosi u datom kontekstu) ili je dostupna, ali ne preko datog imena; u tom slučaju je van konteksta (program je "van opsega imena"). U drugim slučajevima „životni vek“ je irelevantan - nalepnica (imenovana pozicija u izvornom kodu) ima vek trajanja identičan programu (za statički kompajlirane jezike), ali može biti u ili izvan konteksta u određenoj tački programa, i slično za statičke promenljive - statička globalna promenljiva je u kontekstu za ceo program, dok je statična lokalna promenljiva samo u kontekstu neke funkcije ili drugog lokalnog konteksta, ali obe imaju vek trajanja čitavog programa.

Nivoi opsega[uredi | uredi izvor]

Opseg može varirati od samo jednog izraza do čitavog programa, sa mnogo mogućih gradacija između. Najjednostavnije pravilo za ocenjivanje je globalni opseg - svi subjekti su vidljivi tokom celog programa. Najosnovnije modularno pravilo za opseg obuhvata opseg na dva nivoa, sa globalnim opsegom bilo gde u programu i lokalnim opsegom unutar funkcije. Sofisticiranije modularno programiranje omogućava zaseban domet modula, gde su imena vidljiva unutar modula (privatna za modul), ali nisu vidljiva izvan njega. U okviru neke funkcije, neki jezici, kao što je C, dozvoljavaju opseg bloka da ograniči opseg na podskup funkcije; drugi, posebno funkcionalni jezici, omogućavaju opseg izraza da ograniči opseg na jedan izraz. Ostali obim obuhvata opseg datoteka (posebno na C) koji se ponaša slično kao opseg modula i blokira opseg izvan funkcija (posebno na Perlu).

Suptilno pitanje je to kada se opseg započinje i kada se završi. U nekim jezicima, kao što je C, opseg imena započinje njegovom deklaracijom, tako da različita imena deklarisana u određenom bloku mogu imati različite domete. Ovo zahteva deklarisanje funkcija pre upotrebe, mada ih ne mora nužno i definisati, i zahteva prenošenje deklaracije u nekim slučajevima, posebno za međusobnu rekurziju. U ostalim jezicima, kao što su JavaScript ili Python opseg imena počinje na početku relevantnog bloka (poput pokretanja funkcije), bez obzira na to gde je definisan, i sva imena unutar određenog bloka imaju isti opseg; u JavaScript je to poznato kao dizanje promenljivih. Međutim, kada se naziv veže za neku vrednost varira, a ponašanja u kontekstnim imenima koja imaju nedefinisanu vrednost se razlikuju.

Opseg izraza[uredi | uredi izvor]

Mnogi jezici, naročito funkcionalni jezici, nude funkciju koja se zove let-expressions, koja omogućava da opseg deklaracije bude samostalan izraz. To je pogodno ako je, na primer, potrebna jedna srednja vrednost za računanje. Na primer, u standardnom ML  if f() returns 12, then let val x = f() in x * x end je izraz koji ima vrednost 144, koristeći privremenu promenljivu x kako bi se izbeglo dvostruko pozivanje f(). Neki jezici sa opsegom bloka približavaju ovu funkciju nudeći sintaksu za umetanje bloka u izraz; na primer, gore pomenuti standardni ML izraz može se napisati u Perlu kao do { my $x = f(); $x * $x },ili u GNU C kao ({ int x = f(); x * x; }).

U Python-u pomoćne promenljive u izrazima generatora imaju opseg izraza.

U C-u, imena promenljivih u prototipu funkcije imaju opseg izraza, u ovom kontekstu poznat kao opseg protokola funkcije.

Scope block[uredi | uredi izvor]

Mnogi, ali ne svi, programski jezici omogućavaju da se opseg ograniči na blok, koji je poznat i kao opseg bloka. Ovo je počelo ALGOL-om 60, gde „[e] very declaration ... važi samo za taj blok.“[5], a danas je posebno povezana sa jezicima u porodicama i tradicijama Pascal i C. Najčešće se ovaj blok nalazi unutar funkcije, čime se domet ograničava na deo funkcije, ali u nekim slučajevima, kao što je Perl, blok možda nije u funkciji.

unsigned int sum_of_squares(const unsigned int N) {

  unsigned int ret = 0;

  for (unsigned int n = 1; n <= N; n++) {

   const unsigned int n_squared = n * n;

   ret += n_squared;

  }

  return ret;

}


Reprezentativni primer upotrebe blok-opsega je C kod koji je ovde prikazan, gde se dve promenljive dovode u petlju: promenljiva petlje n, koja se inicijalizuje jednom i povećava na svakoj iteraciji petlje, i pomoćna promenljiva n_squared, koja se inicijalizuje pri svakoj iteraciji. Svrha je izbeći dodavanje promenljivih u opseg funkcija koje su relevantne samo za određeni blok - na primer, to sprečava greške kod kojih je generička promenljiva petlje slučajno već postavljena na drugu vrednost. U ovom primeru izraz n * n generalno ne bi bio dodeljen pomoćnoj promenljivoj, a telo petlje bi jednostavno bilo zapisano ret + = n * n, ali u složenijim primerima su korisne pomoćne promenljive.

Blokovi se prvenstveno koriste za kontrolisanje protoka, kao što su if, while, I for petlje, a u tim slučajevima opseg bloka znači da opseg promenljive zavisi od strukture protoka izvršenja funkcije. Međutim, jezici sa opsegom bloka obično omogućavaju i upotrebu „golih“ blokova, čija je jedina svrha omogućavanje fine-grained kontrole promenljivog opsega. Na primer, pomoćna promenljiva se može definisati u bloku, zatim koristiti (recimo, dodati promenljivoj sa opsegom funkcije) i odbaciti kada se blok završi, ili neko vreme petlja može biti zatvorena u bloku koji inicijalizuje promenljive koje se koriste unutar petlje to bi trebalo da bude inicijalizirano samo jednom.

Opseg bloka može se koristiti za senčenje. U ovom primeru, unutar bloka bi se pomoćna promenljiva mogla nazvati i n, senčeći naziv parametra, ali ovo se smatra lošim stilom zbog mogućnosti grešaka. Nadalje, neki potomci C-a, kao što su Java i S# uprkos tome što imaju podršku za blok opseg (pri čemu lokalna promenljiva može biti napravljena da izađe iz dometa pre kraja funkcije), ne dozvoljavaju jednoj lokalnoj promenljivoj da sakrije drugu . U takvim jezicima pokušaj deklaracije drugog n rezultirao bi sintaksnom greškom, a jedna od n promenljivih bi morala biti preimenovana.

Ako se blok koristi za postavljanje vrednosti promenljive, opseg bloka zahteva da se promenljiva deklariše izvan bloka. Ovo usložnjava upotrebu uslovnih izjava sa jednim zadatkom. Na primer, u Python-u, koji ne koristi opseg bloka, može inicijalizovati promenljivu kao takvu:

if c:
    a = 'foo'
else:
    a = ''

gde je a dostupno nakon if uslova. U Perlu koji ima opseg bloka, ovo umesto toga zahteva proglašavanje promenljive pre bloka:

my $a;
if (c) {
  $a = 'foo';
} else {
  $a = '';
}

U Python-u to bi bilo:

a = ''
if c:
    a = 'foo'

Dok bi ovo u Perl-u bilo:

my $a = '';
if (c) {
  $a = 'foo';
}

U slučaju dodeljivanja jedne promenljive, alternativa je upotreba ternarnog operatora da se izbegne blok, ali to nije moguće za dodeljivanja više promenljivih i teško je čitati složenom logikom.

Ovo je značajnije pitanje u C-u, posebno za dodeljivanje niza, kao što inicijalizacija niza može automatski dodeliti memoriju, dok dodeljivanje niza već inicijalizovanoj promenljivoj zahteva dodelu memorije, kopiju niza i proveru da li su uspešni.

Opseg funkcije[uredi | uredi izvor]

Većina programskih jezika nudi način kreiranja lokalne promenljive u funkciji ili potprogramu: promenljiva čiji se opseg završava (koji izlazi iz konteksta) kada se funkcija vrati. U većini slučajeva, životni vek promenljive je trajanje poziva funkcije - to je automatska promenljiva, stvorena kada se funkcija pokrene (ili se promenljiva deklariše), uništi kada se funkcija vrati - dok je opseg promenljive unutar funkcija, mada značenje „unutar“ zavisi od toga da li je opseg leksički ili dinamički.

Važno je da u leksičkom opsegu promenljiva sa opsegom funkcije ima opseg samo unutar leksičkog konteksta funkcije: ona izlazi iz konteksta kada se druga funkcija poziva u funkciji, i vraća se u kontekst kada se funkcija vrati - pozvane funkcije nemaju pristup lokalnim promenljivim pozivajućih funkcija, a lokalne promenljive su samo u kontekstu unutar tela funkcije u kojoj su deklarisane. Suprotno tome, u dinamičkom opsegu opseg se nastavlja na kontekst izvođenja funkcije: lokalne promenljive ostaju u kontekstu kada se poziva druga funkcija, samo se kreću van konteksta kada se funkcija definisanja završi, i zato su lokalne promenljive u kontekstu funkcije u kojoj su definisane i sve se nazivaju funkcijama. U jezicima sa leksičkim opsegom i ugneždenim funkcijama, lokalne promenljive su u kontekstu ugneždenih funkcija jer su unutar istog leksičkog konteksta, ali ne i za ostale funkcije koje nisu leksički ugneždene. Lokalna promenljiva ograđujuće funkcije poznata je kao ne-lokalna promenljiva ugneždene funkcije. Opseg funkcija je takođe primenljiv na anonimne funkcije.

Na primer, u isečku Python koda dole, definisane su dve funkcije: square  i sum_of_squares. square  . Square računa kvadrat broja; sum_of_squares izračunava zbir svih kvadrata.(izračunava Na primer, kvadrat (4) je 42 = 16, a zbir kvadrata je (4) je 02 + 12 + 22 + 32 + 42 = 30).

Svaka od ovih funkcija ima promenljivu sa imenom n koja predstavlja argument funkcije. Ove dve n promenljive su potpuno odvojene i nepovezane, uprkos tome što imaju isti naziv, jer su leksički obuhvaćene lokalne promenljive sa opsegom funkcija: opseg svake promenljive je njen lično, leksički odvojena funkcija i na taj način se ne preklapaju. Zbog toga, sum_of_squares  može pozvati square bez da se njegov n menja. Slično tome, sum_of_squares ima promenljive nazvane total i i; ove promenljive, zbog svog ograničenog opsega, neće ometati nijednu promenljivu pod nazivom total ili i koja bi mogla pripadati bilo kojoj drugoj funkciji. Drugim rečima, ne postoji rizik od sudara imena između ovih identifikatora i bilo kojih nepovezanih identifikatora, čak i ako su identični.

def square(n):
    return n * n

def sum_of_squares(n):
    total = 0 
    i = 0
    while i <= n:
        total += square(i)
        i += 1
    return total

Opseg funkcija je značajno složeniji ako su funkcije objekti prve klase i mogu se lokalno kreirati u funkciju, a zatim vratiti. U ovom slučaju sve promenljive u ugneždenoj funkciji koje joj nisu lokalne stvaraju zatvorenje, ne samo same funkcije, već i njihovo okruženje mora biti vraćeno, a zatim potencijalno pozvano u drugom kontekstu. Ovo zahteva znatno veću podršku od prevodioca i može komplikovati analizu programa.

Opseg datoteke[uredi | uredi izvor]

Pravilo opsega ponajviše određeno za C(i C++) je opseg datoteke, pri čemu je opseg promenljivih i funkcija deklarisanih na vrhu datoteke za celu datoteku - ili bolje za C, od deklaracije do kraja izvorne datoteke, tačnije jedinice za prevođenje (unutrašnje povezivanje). Ovo se može posmatrati kao oblik opsega modula, gde su moduli identifikovani sa datotekama, a na modernijim jezicima je zamenjen eksplicitnim opsegom modula. Zbog prisustva izraza koji uključuju, koji dodaju promenljive i funkcije unutrašnjem kontekstu i mogu se sami pozivati dalje uključuju izjave, što može biti teško odrediti šta je u kontekstu u telu datoteke.

U gornjem isečku C koda, naziv funkcije  sum_of_squares ima opseg datoteke.

Opseg modula[uredi | uredi izvor]

U modularnom programiranju opseg imena može biti čitav modul, ali može biti strukturiran kroz različite datoteke. U ovoj paradigmi moduli su osnovna jedinica složenog programa, jer omogućavaju sakrivanje i izlaganje informacija ograničenom interfejsu.

U nekim objektno-orijentisanim programskim jezicima kojima nedostaje direktna podrška za module, kao što je C++, slična struktura je umesto nje obezbeđena hijerarhijom klasa, gde su klase osnovna jedinica programa, a klasa može imati privatne metode. To se pravilno razume u kontekstu dinamičkog slanja, umesto rezolucije i opsega imena, mada često igraju analogne uloge. U nekim slučajevima su dostupna oba objekta, kao što je na primer Python koji ima i module i klase, a organizacija koda (kao funkcija na nivou modula ili konvencionalno privatna metoda) je izbor programera.

Globalni opseg[uredi | uredi izvor]

Deklaracija ima globalni opseg ako deluje na čitav program. Imena promenljivih sa globalnim opsegom - nazvane globalne promenljive - često se smatraju lošom praksom, bar u nekim jezicima, zbog mogućnosti sudaranja imena i nenamernog maskiranja, zajedno sa slabom modularnošću i opsegom funkcija ili opsegom bloka, smatraju se poželjnijim. Međutim, globalni opseg se obično koristi za razne druge vrste identifikatora, poput naziva funkcija i imena klasa i drugih vrsta podataka. U ovim slučajevima mehanizmi poput prostora s imenima se koriste kako bi se izbegli sudari.

Leksički opseg nasuprot dinamičkom ospegu[uredi | uredi izvor]

Upotreba lokalnih promenljivih - imena promenljivih sa ograničenim opsegom, koja postoje samo u okviru određene funkcije - pomaže da se izbegne rizik od sudara imena dve identično imenovane promenljive. Međutim, postoje dva vrlo različita pristupa odgovoru na ovo pitanje: Šta znači biti u okviru funkcije?

U leksičkom opsegu, ako je opseg imena promenljive određena funkcija, onda je njegov opseg programski tekst definicije funkcije: unutar tog teksta, ime promenljive postoji i vezano je  za vrednost promenljive, ali izvan tog teksta, ime promenljive ne postoji. Suprotno tome, u dinamičkom opsegu, ako je opseg imena promenljive određena funkcija, onda je njegov opseg vremenski period tokom koga se funkcija izvršava: dok se funkcija pokreće, ime promenljive postoji i jeste vezano za svoju vrednost, ali nakon što se funkcija vrati, naziv promenljive ne postoji. To znači da ako funkcija f poziva posebno definisanu funkciju g onda pod leksičkim opsegom funkcija g nema pristup lokalnim promenljivama f dok pod dinamičkim opsegom funkcija g ima pristup lokalnim promenljivim f .

$ # bash language
$ x=1
$ function g () { echo $x ; x=2 ; }
$ function f () { local x=3 ; g ; }
$ f # does this print 1, or 3?
3
$ echo $x # does this print 1, or 2?
1

Razmatramo,na primer program iznad.  Prva linija x=1 kreira globalnu promenljivu x i inicijalizuje je na 1. Druga linija function g () { echo $x ; x=2 ; } definiše funkciju g koja štampa ("echoes") trenutnu vrednost x, a zatim postavlja x  na 2. Treća linija, function f () { local x=3 ; g ; }  definiše funkciju f koja kreira lokalnu promenljivu x, i inicijalizuje je na 3. Četvrta linija, f poziva f. Peta linija, echo $x štampa trenutnu vrednost x.

Dakle, šta tačno štampa ovaj program? To zavisi od pravila za opseg. Ako je jezik ovog programa onaj koji koristi leksički opseg, tada g štampa i modifikuje globalnu promenljivu x (jer je g definisan izvan f), tako da program štampa 1, a zatim 2. Za razliku od toga, ako ovaj jezik koristi dinamički opseg, onda g ispisuje i menja lokalnu promenljivu x funkcije f(jer se g poziva iz f), tako da program štampa 3, a zatim 1.

Leksički opseg[uredi | uredi izvor]

Leksički opseg se odnosi na lokalno leksičko okruženje.Ovo je svojstvo programskog teksta, a prilikom implementacije izvršavanje se odigrava nezavisno od skupa poziva.Leksički opseg se još naziva i „statički opseg“ . Kod jezika koji su zasnovani na  ALGOL-u kao što su  Pascal, Modula-2 i Ada leksički opseg je standardan,ali pored njih zastupljen je i u modernim funkcionalnim jezicima kao što su ML and Haskell . Statički obuhvaćeni jezici među kojima je i S se razlikuju po nivou ograničenosti. Statički opseg dozvoljava programeru da razmišlja o objektima referenci, kao što su parametri, promenljive, konstante, vrste, funkcije itd, kao jednostavnim zamenama imena.To predstavlja veliko olakšanje prilikom pravljenja modularnog koda.

Npr. leksički obuhvaćen je i Pascal, razmislite o kodu koji je napisan u programskom jeziku Pascal

program A;
var I:integer;
    K:char;

    procedure B;
    var K:real;
        L:integer;

        procedure C;
        var M:real;
        begin
         (*scope A+B+C*)
        end;

     (*scope A+B*)
    end;

 (*scope A*)
end.

Promenljiva I je vidljiva u svim tačkama, jer nikada nije sakrivena od strane druge istoimene promenljive.Za razliku od I, promenljiva К je vidnjiva samo u glavnom programu, jer je skrivena od strane stvarne promenljive К koja je vidljiva samo u procedurama B i C. Poput promennjive К, promenljiva L, ali ne skriva ostale promenljive. Promenljiva М je vidljiva samo u proceduri C, iz tog razloga nije je moguće pozvati ni iz glavnog programa,  a ni iz postupka B. Takođe, procedura S je vidljiva samo u proceduri B i zbog toga je ne možete pozvati iz glavnog programa.

Mesto u programu gde se pominje "C" tada određuje koji od dva postupka pod nazivom C se koristi tačno.

Ispravna primena statičkog opsega  u jezicima sa prvoklasnim ugneždenim funkcijama nije trivijalna, jer zahteva da svaka vrednost funkcije nosi sa sobom zapis vrednosti promenljivih od kojih zavisi. U zavisnosti od implementacije i arhitekture računara, pretraživanje može postati neefikasno  kada se koriste veoma duboko leksički ugnežđene funkcije, mada postoje dobro poznate tehnike za njihovo ublažavanje. Funkcije koje se odnose na vlastite argumente i lokalne promenljive, sve lokacije mogu biti poznate za vreme kompilacije.Samim tim ne pojavljuju se režijski troškovi pri korišćenju te funkcije, to se odnosi i na delove programa u kojima se ove funkcije ne koriste, ali i na programe kod kojih one nisu dostupne.

Istorija[uredi | uredi izvor]

Leksički opseg korišćen je za imperativni jezik ALGOL60 i od tada je počeo da se koristi u većini ostalih imperativnih jezika.

Jezici poput Pascal i C oduvek su imali leksički opseg, dok je Perl jezik sa dinamičkim opsegom.

Originalni Lisp prevodilac (1960) koristio je dinamički opseg. Duboko vezivanje, koje približava statički (leksički) opseg, uvedeno je u Lisp 1.5 (preko Funarg uređaja koji je razvio Steve Russell).

Svi rani Lispovi koristili su dinamiči opseg, barem na osnovu tumača. Godine 1982. Guy L. Steele Jr. i Common LISP Group objavljuju An overview of Common LISP,[6] kratak pregled istorije i divergentnih implementacija Lispa do tog trenutka i pregled karakteristika koje zajednički Lisp primena treba da ima. Na stranici 102 čitamo:

Većina LISP implementacija interno je nedosledna u tome što po tumačenju prevodilac i kompajler mogu dodeliti različite semantike za ispravljanje programa; ovo proizilazi pre svega iz činjenice da tumač pretpostavlja da su sve promenljive dinamički obuhvaćene, dok prevodilac pretpostavlja da su sve promenljive lokalne, osim ako nisu primorani da pretpostave drugačije. To je učinjeno radi praktičnosti i efikasnosti, ali može dovesti do veoma suptilnih grešaka. Definicija Common LISP-a izbegava takve anomalije izričito zahtevajući od prevodioca i kompajlera da nameću identičnu semantiku ispravnim programima.

Implementacija Common LISP-a je zbog toga trebala imati leksički opseg. Ponovo iz An overview of Common LISP:

Pored toga, Common LISP nudi sledeće pogodnosti. Potpuno leksički obuhvaćene promenljive. Takozvani „problem FUNARG“ [7]potpuno je rešen, i u slučaju gore i dole.

Iste godine u kojoj je objavljen An overview of Common LISP  (1982), objavljeni su početni dizajni (takođe Gui L. Steele Jr.) sastavljenog, leksički obuhvaćenog Lispa, nazvanog Scheme. U to vreme  leksički opseg u Lispu strahuje da nije efikasan za sprovođenje.

Izraz "leksički opseg" datira najmanje od 1967.[8] [9]godine, dok termin "leksički opseg" datira bar od 1970. godine, gde je korišćen u Project MAC-u za opis pravila opsega  Lispovog dijalekta MDL.[10]

Dinamički opseg[uredi | uredi izvor]

Sa dinamičkim opsegom, globalni identifikator odnosi se na identfikator povezan sa najnovijim okruženjem i neuobičajen je u savremenim jezicima. Tehnički gledano, to znači da svaki identifikator ima globalni niz vezivanja. Uvođenjem lokalne promenljive sa nazivom x gura vezivanjeu globalni stek, koji iskače kada kontrolni tok napusti opseg. Procena x u bilo kom kontekstu uvek daje gornju obavezu. Imajte na umu da se to ne može izvršiti u vreme kompajliranja, jer vezivanje seta postoji samo u vreme izvođenja, zbog čega se ova vrsta vezivanja naziva dinamičkim opsegom.

Generalno, određeni blokovi su definisani tako da stvaraju vezivanja čiji je životni vek vreme izvršenja bloka; ovo dodaje neke karakteristike statičkog opsega u proces dinamičkog opsega. Međutim, budući da se deo koda može pozvati sa mnogo različitih lokacija i situacija, na početku može biti teško odrediti koja će se vezivanja primenjivati kada se koristi promenljiva. Ova uska interpretacija deljenih podataka može pružiti vrlo fleksibilan sistem za prilagođavanje ponašanja funkcije u trenutnom stanju sistema. Međutim, ova korist se oslanja na pažljivu dokumentaciju svih promenljivih koji se koriste na ovaj način, kao i na pažljivo izbegavanje pretpostavki o ponašanju promenljive i ne pruža nikakav mehanizam za otkrivanje smetnji između različitih delova programa. Dinamički opseg takože poništava sve vrednosti referentne transparentnosti. Kao takav, dimački opseg može biti opasan i malo savremenih jezika ga koristi. Neki jezici, poput Perl iCommon Lisp omogućavaju programeru da odabere statički ili dinamički opseg prilikom definisanja ili redefinisanja promenljive. Primeri jezika koji koriste dinamičko ocenjivanje uključuju  Logo, Emacs Lisp, LaTeX i jezike bash, dash, and PowerShell.

Dinamički opseg je prilično lako implementirati. Da bi pronašao vrednost identifikatora, program bi mogao da pređe vreme izvršavanja steka, proveravajući da li svaki zapis aktivacije ima vrednost za identifikator. U praksi se to postiže efikasnije korišćenjem asocijativne liste, koja predstavlja stek parova naziva/ vrednosti. Parovi se guraju na ovaj stek kad god se deklaracije izgrade i iskaču kad god promenljive izađu iz opsega.[11] Plitko vezivanje je alternativna strategija koja je znatno brža i koja koristi centralnu referentnu tabelu koja svako ime povezuje sa sopstvenim stekom značenja. Ovim se izbegava linearna pretraga tokom izvršavanja da bi se pronašao određeni naziv, ali treba voditi računa o pravilnom održavanju ove tabele. Imajte na umu da obe ove strategije pretpostavljaju poslednji-prvi-izlaz (LIFO) da bi naredio povezivanje za bilo koju promenljivu; u praksi su sve veze tako naručene.

Još jednostavnija implementacija je predstavljanje dinamičkih promenljivih sa jednostavnim globalnim promenljivama. Lokalno vezivanje se izvodi tako što se originalna vrednost sačuva na anonimnoj lokaciji na steku koji je programu nevidljiv. Kada se taj opseg vezivanja prekine, izvorna vrednost se vraća sa ove lokacije. U stvari, dinamički opseg je nastao na ovaj način. Rane implementacije Lisp-a koristile su ovu očiglednu strategiju za implementaciju lokalnih promenljivih, a praksa opstaje u nekim dijalektima koji su i dalje u upotrebi, kao što je GNU Emacs Lisp. Kasnije je u Lisp uveden leksički opseg. To je ekvivalentno gornjoj plitkoj šemi vezivanja, osim što je centralna referentna tabela jednostavno globalno okruženje, u kojoj je trenutno značenje promenljive njena globalna vrednost. Održavanje globalnih promenljivih nije složeno. Na primer, simbolni objekt može imati namenski slot za svoju globalnu vrednost.

Dinamički opseg pružaodličnu apstrakciju za lokalno skladištenje niti, ali ako se koristi na tak način, ne može se zasnivati na čuvanju i vraćanju globalne promenljive. Moguća strategija implementacije je da svaka promenljiva lokalni ključ niti. Kada se pristupi promenljivoj, ključ lokalnog navoja koristi se za pristup memoriji lokalne niti. Ako lokalni ključ niti ne postoji za nit koja se poziva, koristi se globalna lokacija. Kada je promenljiva lokalno vezana, prethodna vrednost se čuva na skrivenoj lokaciji na steku. Lokalna memorija navoja kreirana je pod ključem promenljive, a nova vrednost je smeštena tamo. Dalje ugneždena poništavanja promenljive unutar te niti jednostavno se čuvaju i restartuju ovu lokaciju. Kada se početni, spoljašnji najviše nadjačavajući opseg prekine, lokalni ključ nit se briše, izlažući globalnu verziju promenljive još jednom toj niti.

Makro ekspanzija[uredi | uredi izvor]

U modernim jezicima, makro ekspanzija u pretprogramu je ključni primer de facto dinamičkog opsega. Sam jezik makro naredbe transformiše izvorni kod, ali pošto se proširenje vrši na mestu, gde se imena u proširenom tekstu rešavaju, oni se rešavaju na osnovu mesta na kome su prošireni, kao da se događa dinamički opseg.

Potprogram u S, koji se koristi za makro proširenje, ima de facto dinamički opseg, jer sam po sebi ne donosi rezoluciju imena. Na primer, makro:

#define ADD_A(x) x + a

će se proširiti za dodavanje a prenesenoj promenljivoj, a ovaj identifikator će kompajler kasnije razrešiti tek na osnovu mesta gde je makro ADD_A  „pozvan“ u dinamičkom opsegu i ne zavisi od mesta gde je makro definisan. Ispravno, C preprocesor radi samo leksičku analizu, proširujući makro naredbu tokom faze tokenizacije. Na primer, u sledećem kodu makro a u makro se razrešava na lokalnu promenljivu na mestu proširenja:

#define ADD_A(x) x + a

void add_one(int *x) {
  const int a = 1;
  *x = ADD_A(*x);
}

void add_two(int *x) {
  const int a = 2;
  *x = ADD_A(*x);
}

Kvalifikovani identifikatori[uredi | uredi izvor]

Kao što smo videli, jedan od ključnih razloga opsega je taj što pomaže u sprečavanju sudara imena, omogućavajući identičnim identifikatorima da se odnose na različite stvari, uz ograničenje da identifikatori moraju imati zasebne domete. Ponekad je ovo ograničenje neprijatno; kada mnogo različitih stvari mora biti dostupno kroz program, uglavnom im je potreban identifikator globalnog opsega, pa su potrebne različite tehnike da se izbegne sudaranje imena.

Da bi se ovo rešilo, mnogi jezici nude mehanizme za organizovanje globalnih identifikatora. Detalji ovih mehanizama i korišćeni pojmovi zavise od jezika; ali opšta ideja je da grupa identifikatora može i sama sebi  dati ime - prefiks – i. Normalno će takvi identifikatori imati, u određenom smislu, dva skupa opsega: opseg (obično globalni opseg) u kojem je kvalifikovani identifikator vidljiv i jedan ili više užih opsega u kojima je nekvalifikovani identifikator (bez prefiksa) vidljiv takođe. I obično se ove grupe mogu i same organizovati u grupe; to jest, mogu se ugnezditi.

Iako mnogi jezici podržavaju ovaj koncept, detalji se uveliko razlikuju. Neki jezici imaju mehanizme, kao što su namespaces  u C++ and C# koji služe gotovo isključivo da bi se omogućilo organizovanje globalnih identifikatora u grupe. Drugi jezici imaju mehanizme, kao što su paketi u Adi i strukture u Standardnom ML-u, koji to kombinuju sa dodatnom svrhom omogućavanja da neki identifikatori budu vidljivi samo ostalim članovima njihove grupe. I objektno orijentisani jezici često dopuštaju da klase ili singleton objekti ispunjavaju ovu svrhu (bez obzira da li oni imaju i mehanizam za koji je to glavna svrha). Jezici često spopadaju ove pristupe; na primer, Perl paketi su u velikoj meri slični prostorima imena C++ ali opciono se dupliraju kao klase za objektno orijentisano programiranje; i Java organizuje svoje promenljive i funkcije u klase, ali ih zatim organizuje u pakete slične Adi.

Po jeziku[uredi | uredi izvor]

S[uredi | uredi izvor]

U S-u opseg je tradicionalno poznat kao povezivanje ili vidljivost, posebno za promenljive. S je leksički obiman jezik sa globalnim opsegom (poznat kao spoljna veza), opeg modula ili opseg datoteke (poznat kao unutrašnja veza) i lokalni opseg (u okviru funkcije); u okviru funkcija opseg se može dalje ugnezditi preko blok-opsega. Međutim, standardni S ne podržava ugnezdene funkcije.

Životni vek i vidljivost promenljive određuje se njenom klasom čuvanja. Postoje tri vrste životnih vekova u S-u: statički (izvršenje programa), automatski (izvršavanje blokova, dodeljeno na hrpi) i ručni (dodeljen na hrpi). Samo su statički i automatski podržani  za promenljive i kojima kompajler rukuje, dok ručno dodeljena memorija mora biti ručno praćena kroz različite promenljive. U S-upostoje tri nivoa vidljivosti: spoljna veza (globalna), unutrašnja veza (otprilike datoteka) i opseg bloka (koji uključuje funkcije). Unutrašnja povezanost na S-u je vidljivost na nivou prevodilačke jedinice, naime izvorna datoteka nakon što je S preprocesor obrađivao, posebno uključujući sve relevantne obuhvata.

S programi se kompajliraju kao zasebne objektne datoteke, koje se potom povezuju u izvršnu datoteku ili biblioteku putem povezivača. Tako se rezolucija imena deli širom kompajlera, koji rešava imena unutar prevoditeljske jedinice i veznik, koji rešava imena po prevodilačkim jedinicama; vidi vezu za dalju diskusiju.

U S-u promenljive sa opsegom bloka ulaze u opseg kada su deklarisane (nisu na vrhu bloka), izlaze iz područja opsega ako se u bloku poziva bilo koja funkcija, vraćaju se nazad u opseg kad se funkcija vrati, i izlazi van opsega na kraju bloka. U slučaju automatskih lokalnih promenljivih, oni se takođe alociraju na deklaraciji i raspoređuju na kraju bloka, dok se za statičke lokalne promenljive dodeljuju kod inicijalizacije programa i dealociraju po završetku programa.

Sledeći program demonstrira promenljivu, sa opsegom bloka koji ulazi u opseg delom kroz blok, a zatim izlazi iz opsega kada se blok završi:

#include <stdio.h>
int main(void)
{
   char x = 'm';
   printf("%c\n", x);
   {
       printf("%c\n", x);
       char x = 'b';
       printf("%c\n", x);
   }
   printf("%c\n", x);
}
Излаз из програма
m
m
b
m

Postoje i drugi nivoi opsega u S-u.[12] Imena promenljivih koja se koriste u prototipu funkcije imaju vidljivost prototipa funkcije i opseg izlaza na kraju prototipa funkcije. Pošto se ime ne koristi, ovo nije korisno za kompilaciju, ali može biti korisno za dokumentaciju. Nazivi oznaka za GOTO izraz imaju opseg funkcije, dok nazivi slučajeva za izjave prekidača imaju opseg bloka.

C++[uredi | uredi izvor]

Sve promenljive koje nameravamo da koristimo u programu moraju biti deklarisane sa svojim tipom specifikatora u ranijoj tački koda, kao što smo to radili u prethodnom kodu na početku tela glavne funkcije kada smo proglasili da b, i rezultat su tipa int. Promenljiva može biti globalnog ili lokalnog opsega. Globalna promenljiva je promenljiva koja je deklarisana u glavnom telu izvornog koda, izvan svih funkcija, dok je lokalna promenljiva deklarisana u telu funkcije ili bloku.

Go[uredi | uredi izvor]

Go je leksički obuhvaćen pomoću blokova.

Java[uredi | uredi izvor]

Klasa u Javi može imati 3 tipa promenljivih.[13]

Lokalne promenljive su definisane unutar metode, ili unutar partikularnog bloka. Ove promenljive su lokalne do mesta gde su definisan. Na primer, petlja unutar metode može koristiti lokalne promenljive te metode, ali ne i obrnuto. Promenljive petlje (lokalne za tu petlju) se uništavaju čim se petlja završi.

Promenljive članice takođe se nazivaju i poljima, su promenljive deklarisane unutar klase, izvan bilo koje metode. Ove promenljive su podrazumevano dostupne za sve metode unutar te klase kao i za sve klase u paketu.

Parametri su promenljive u deklaraciji metode.

Uopšteno, skup zagrada definiše određeni opseg, ali promenljive na najvišem nivou unutar klase mogu se razlikovati u ponašanju u zavisnosti od ključnih reči modifikatora koji su korišteni u njihovoj definiciji. Sledeća tabela pokazuje pristup članovima koje dozvoljava svaki modifikator.[14]

JavaScript[uredi | uredi izvor]

JavaScript ima jednostavna pravila za opseg,[15] ali inicijalizacija promenljive i pravila rezolucije imena mogu prouzrokovati probleme, a široka upotreba zatvorenja za povratne pozive znači da se leksičko okruženje funkcije kada je definisano (koje se koristi za razrešavanje imena) može jako razlikovati od leksičkog okruženje kada se poziva. JavaScript objekti imaju rezoluciju imena za podešavanja, ali ovo je zasebna tema.

JavaScript ima leksički opseg[16] ugnežden na funkcijkom nivou, pri čemu je globalni opseg krajnji opseg. Ovaj opseg koristi se za i z apromenljive i za funkcije. Blokiranje opsega pomoću ključnih reči let i const je standardno od ECMAScript 6.. Blok-opseg se može proizvesti tako što se ceo blok zamota u funkciju, a zatim izvršiti; ovo je poznato kao obrazac izraza funkcije (IIFE) koji se odmah poziva.

Iako je opseg JavaScript-a jednostavan - leksički, na nivou funkcije - pridružena pravila za inicijalizaciju i rešavanje imena uzrokuju zbrku. Prvo, dodeljivanje imenu koje nije u opsegu podrazumeva kreiranje nove globalne promenljive, a ne lokalne. Drugo, da biste kreirali novu lokalnu promenljivu, morate koristiti ključnu reč var; promenljiva se tada kreira na vrhu funkcije, pri čemu je vrednost nedefinisana i promenljivoj se dodeljuje njena vrednost kada se postigne izraz dodele.

Na primer, sledeći kod proizvodi dijalog sa nedefinisanim izlazom, budući da je deklaracija lokalne promenljive podignuta, senčivši globalnu promenljivu, ali inicijalizacija nije, tako da je promenljiva nedefinisana kada se koristi:

a = 1;
function f() {
  alert(a);
  var a = 2;
}
f();

Nadalje, kako su funkcije prvoklasni objekti u JavaScript-u i često se dodeljuju kao povratni pozivi ili se vraćaju iz funkcija, kada se funkcija izvrši, rezolucija imena zavisi od mesta gde je prvobitno definisana a ne leksičko okruženje ili izvršno okruženje odakle se poziva. Ugneždeni opsezi određeni u JavaScript-u, posebno zatvorenja, koji se koristi kao povratni poziv, ponekad se nazivaju i lancem opsega, analogno prototipnom lancu objekta. Zatvorenja se mogu proizvesti u JavaScript-u korišćenjem ugneždenih funkcija, jer su funkcije prvoklasni objekti.[17] Vraćanje ugneždene funkcije iz ograđujuće funkcije uključuje lokalne promenljive funkcije ograđivanja kao leksičko okruženje vraćene funkcije, što dovodi do zatvorenja. Na primer:

function newCounter() {
  // brojac koji se inkrementira na svaki poziv(pocinje od 0) 
  // i vraca novodobijenu vrednost
  var a = 0;
  var b = function() { a++; return a; };
  return b;
}
c = newCounter();
alert(c() + ' ' + c());  // izlazi "1 2"

Zatvorenja se često koriste u JavaScript-u jer se koriste za povratne pozive. Zapravo, svako spajanje funkcije u lokalnom okruženju kao povratni poziv ili vraćanje iz funkcije stvara zatvorenje ako u funkcionom telu postoji bilo koja nevezana  promenljiva, ovo može biti slučajno. Prilikom kreiranja povratnog poziva na osnovu parametara, parametri se moraju čuvati u zatvorenju, inače će slučajno stvoriti zatvorenje koje se odnosi na promenljive u okruženju, koje se mogu promeniti.[18]

Lisp[uredi | uredi izvor]

Lisp dijalekti imaju razna pravila za opseg.

Originalni Lisp je koristio dinamički opseg inspirisan Algolom.

Maclisp je podrazumevano koristio dinamički opseg u interpretatoru i leksički opseg u kompajliranom kodu, mada je kompajlirani kod mogao da pristupi dinamičkim vezama koristeći sprecijalne deklaracije za određene promenljive.[19] Međutim Maclisp je leksičko vezivanje tretirao više kao optimizaciju nego što bi se to moglo očekivati u modernim jezicima. Odvojena operacija, *FUNCTION, bila je dostupna za nespretno rešavanje nekih problema.[20]

Common Lisp je usvojio leksički opseg iz Scheme.[21]

ISLISP ima leksički opseg za obične promenljive. Takođe ima dinamičke promenljive, ali su u svim slučajevima eksplicitno obeležene; moraju biti definisani posebnim specijalnim oblikom.

Neki drugi dijalekti Lispa, poput Emacs Lisp i dalje koriste dinamički opseg prema zadanim postavkama. Emacs Lisp sada ima na raspolaganju leksički opseg na bazi bafera.[22]

Python[uredi | uredi izvor]

Za promenljive, Pajton ima opseg funkcija, opseg modula i globalni opseg. Imena ulaze u opseg na početku konteksta, a izlaze iz opsega kada se poziva ne-ugneždena funkcija ili se kontekst završava. Ako se ime koristi pre inicijalizacije promenljive, to stvara izuzetak tokom izvođenja. Ako se nekoj promenljivoj jednostavno pristupi u kontekstu, rezolucija imena sledi LEGB pravilo. Međutim, ako je promenljiva dodeljena, ona podrazumeva stvaranje lokalne promenljive, koja je u opsegu za ceo kontekst. Oba ova pravila mogu se nadjačati globalnom ili ne-lokalnom deklaracijom pre upotrebe, što omogućava pristup globalnim promenljivim čak i ako postoji intervenirajuća ne-lokalna promenljiva, i dodeljivanje globalnih ili nelokalnih promenljivih.

Kao jednostavan primer, funkcija rešava promenljivu u globalnom opsegu:

>>> def f():
... print(x)
...
>>> x = 'global'
>>> f()
global

Imajte na umu da je x inicijaliziran pre nego što se pozove f, tako da se ne podiže greška, iako je deklarisana nakon deklarisanja f. Leksički je ovo referenca unapred, što je dozvoljeno u Python-u. Ovde zadatak stvara novu lokalnu promenljivu, koja ne menja vrednost globalne promenljive:

>>> def f():
...     x = 'f'
...     print(x)
...
>>> x = 'global'
>>> print(x)
global
>>> f()
f
>>> print(x)
global

Dodeljivanje promenljivih unutar funkcije uzrokuje da se ona proglasi lokalnom za funkciju, pa na taj način korišćenjem pre dodele javlja grešku. Ovo se razlikuje od C-a, gde je lokalna promenljiva samo u deklaraciji, a ne za celu funkciju. Ovaj kod stvara grešku:

>>> def f():
...     print(x)
...     x = 'f'
...
>>> x = 'global'
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment

Podrazumevana pravila za rezoluciju imena mogu se nadjačati globalnom ili nelokalnom ključnom rečju. U kodu ispod, globalna deklaracija x u g znači da se x slaže sa globalnom promenljivom. Tako mu se može pristupiti (kao što je već inicijalizirano) i dodeljivanju se dodeljuje globalna promenljiva, umesto da se deklariše nova lokalna promenljiva. Imajte na umu da nije potrebna globalna deklaracija u f-budući da ne dodeljuje promenljivoj, ona podrazumeva rezoluciju za globalnu promenljivu. Imajte na umu da nije potrebna globalna deklaracija u f ; budući da se ne  dodeljuje promenljivoj, ona podrazumeva rešavanje za globalnu promenljivu.

>>> def f():
...     print(x)
...
>>> def g():
...     global x
...     print(x)
...     x = 'g'
...
>>> x = 'global'
>>> f()
global
>>> g()
global
>>> f()
g


global  se takođe može koristiti za ugneždene funkcije. Pored toga što omogućava dodeljivanje globalnoj promenljivoj, kao u ne ugneždenoj funkciji, ovo se takođe može koristiti za pristup globalnoj promenljivoj u prisustvu nelokalne promenljive:

>>> def f():
... def g():
... global x
... print(x)
... x = 'f'
... g()
...
>>> x = 'global'
>>> f()
global

Za ugneždene funkcije postoji i nonlocal deklaracija za dodeljivanje ne-lokalnoj promenljivoj, slično upotrebi global  u ne ugneždenoj funkciji:

>>> def f():
... def g():
... nonlocal x  # Python 3 only
... x = 'g'
... x = 'f'
... g()
... print(x)
...
>>> x = 'global'
>>> f()
g
>>> print(x)
global

R[uredi | uredi izvor]

R je leksički obiman jezik, za razliku od drugih implementacija S-a gde su vrednosti slobodnih promenljivih određene skupom globalnih promenljivih, dok u R određuju okruženje u kome je funkcija kreirana.Okolini za opseg pristupa može se pristupiti korišćenjem različitih funkcija koje mogu da simuliraju iskustvo dinamičkog opsega ako programer poželi.

no block scoping

>>> def f():
... def g():
... nonlocal x  # Python 3 only
... x = 'g'
... x = 'f'
... g()
... print(x)
...
>>> x = 'global'
>>> f()
g
>>> print(x)
global

functions have access to environment (scope) they were created in

a <- 1

f <- function() {message(a)}

f()

## 1

variables created or modified within a function stay there

a <- 1
f <- function() {
  message(a)
  a <- 2
  message(a)
}
f()
## 1
## 2
message(a)
## 1

variables created or modified within a function stay there unless assignment to enclosing environment (scope) is explicitly requested

a <- 1
f <- function() {
  message(a)
  a <<- 2
  message(a)
}
f()
## 1
## 2
message(a)
## 2

although R has lexical scoping by default, function scopes can be changed

a <- 1
f <- function() {
  message(a)
}
my_env <- new.env()
my_env$a <- 2
f()
## 1
environment(f) <- my_env
f()
## 2

Pogledaj još[uredi | uredi izvor]

Reference[uredi | uredi izvor]

  1. ^ C99 (na jeziku: engleski), 2020-07-14, Pristupljeno 2020-08-26 
  2. ^ „The Go Programming Language Specification - The Go Programming Language”. golang.org. Pristupljeno 2020-08-26. 
  3. ^ „CSE 341 Lecture Notes -- Lexical and Dynamic Scoping”. web.archive.org. 2015-02-07. Arhivirano iz originala 07. 02. 2015. g. Pristupljeno 2020-08-26. 
  4. ^ „AngularJS”. docs.angularjs.org. Pristupljeno 2020-08-26. 
  5. ^ W, BackusJ; L, BauerF; GreenJ; KatzC; McCarthyJ; J, PerlisA; RutishauserH; SamelsonK; VauquoisB (1960-05-01). „Report on the algorithmic language ALGOL 60”. Communications of the ACM (na jeziku: engleski). doi:10.1145/367236.367262. 
  6. ^ „An overview of COMMON LISP | Proceedings of the 1982 ACM symposium on LISP and functional programming”. dl.acm.org (na jeziku: engleski). doi:10.1145/800068.802140. Pristupljeno 2020-08-26. 
  7. ^ Moses, Joel (1970-06-01). „The Function of FUNCTION in LISP, or Why the FUNARG Problem Should be Called the Environment Problem” (na jeziku: engleski). 
  8. ^ Conferences, University of Michigan Engineering Summer (1967). Computer and Program Organization (na jeziku: engleski). 
  9. ^ Conferences, University of Michigan Engineering Summer (1967). Computer and Program Organization (na jeziku: engleski). 
  10. ^ Technology), Project MAC (Massachusetts Institute of (1970). Project MAC Progress Report (na jeziku: engleski). Massachusetts Institute of Technology. 
  11. ^ „Programming Language Pragmatics”. www.cs.rochester.edu. Pristupljeno 2020-08-26. 
  12. ^ „IBM Knowledge Center - Home of IBM product documentation”. www.ibm.com (na jeziku: engleski). Pristupljeno 2020-08-26. 
  13. ^ „Declaring Member Variables (The Java™ Tutorials > Learning the Java Language > Classes and Objects)”. docs.oracle.com. Pristupljeno 2020-08-26. 
  14. ^ „Controlling Access to Members of a Class (The Java™ Tutorials > Learning the Java Language > Classes and Objects)”. docs.oracle.com. Pristupljeno 2020-08-26. 
  15. ^ „Annotated ES5”. es5.github.io. Pristupljeno 2020-08-26. 
  16. ^ „Functions”. MDN Web Docs (na jeziku: engleski). Pristupljeno 2020-08-26. 
  17. ^ „Javascript Closures”. jibbering.com. Pristupljeno 2020-08-26. 
  18. ^ „Explaining JavaScript scope and closures - Robert's talk”. robertnyman.com. Pristupljeno 2020-08-26. 
  19. ^ „The Pitmanual: Declarations and the Compiler”. maclisp.info. Pristupljeno 2020-08-26. 
  20. ^ „The Pitmanual: The Evaluator”. www.maclisp.info. Pristupljeno 2020-08-26. 
  21. ^ „CLHS: Section 1.1.2”. www.lispworks.com. Pristupljeno 2020-08-26. 
  22. ^ „EmacsWiki: Lexical Binding”. www.emacswiki.org. Pristupljeno 2020-08-26.