V računalništvu je zaključek (angleško closure) funkcija, ki ima svoje okolje — to pomeni, da poleg telesa funkcije shrani tudi referenco na spremenljivke iz zunanjega obsega (vsaj ena take spremenljivke je vezana/prosta glede na to funkcijo). Okolje zaključka ohranja te vezane spremenljivke v pomnilniku in jih omogoča uporabljati tudi potem, ko je izvršitev zunanje funkcije že končana.
Kako deluje zaprtje
Zaprtje nastane, ko znotraj funkcije ustvarimo drugo (notranjo) funkcijo, ta notranja funkcija pa sklicuje spremenljivke iz zunanjega obsega. Ko zunanja funkcija vrne notranjo funkcijo, se običajno pričakuje, da bodo zunanje lokalne spremenljivke izginile; pri zaprtju pa te spremenljivke ostanejo dostopne preko notranje funkcije, ker jih okolje zaključka ohrani.
- Vezane (bound) spremenljivke: imena, ki jih notranja funkcija uporablja in so definirana v zunanjem obsegu.
- Proste (free) spremenljivke: spremenljivke, ki niso lokalne notranji funkciji, a so v njenem dosegu zaradi okolja.
- Levkonsko (lexical) skopanje: večina jezikov z zaprtji uporablja leksikalno skopanje — okolje se določi glede na strukturo kode v času pisanja (napram dinamičnemu skopanju).
Kratek zgodovinski kontekst
Peter J. Landin je leta 1964 to zamisel poimenoval Closure. Programski jezik Scheme je po letu 1975 pomembno prispeval k širitvi uporabe zaprtij. Danes številni sodobni programski jeziki (JavaScript, Python, Ruby, Lisp, Haskell, in mnogi drugi) podpirajo zaključke.
Primeri
Tipičen primer zaprtja v JavaScriptu (preprosti števec):
function makeCounter() { let count = 0; return function() { count++; return count; }; } const c = makeCounter(); console.log(c()); // 1 console.log(c()); // 2 V tem primeru notranja anonimna funkcija ohranja dostop do spremenljivke count, tudi ko je makeCounter že vrnila rezultat.
Podoben primer v Pythonu z uporabo nonlocal (za spreminjanje zunanje spremenljivke):
def make_counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = make_counter() print(c()) # 1 print(c()) # 2 Pogoste napačne predstave
- Anonomne funkcije niso vedno zaprtja: Anonimne funkcije so funkcije brez imena, vendar so zaprtja le, če resnično ujemajo okolje (tj. uporabljajo spremenljivke iz zunanjih obsegov). Anonimna funkcija brez lastnega okolja ni zaprtje.
- Poimenovana funkcija je lahko zaprtje: zaprtje ni sinonim za anonimnost; tudi poimenovana funkcija lahko tvori zaprtje, če ohranja zunanji kontekst.
- Zajemanje po vrednosti ali referenci: načelo zajemanja je odvisno od jezika — nekateri jeziki (npr. JavaScript) zajemajo referenco na spremenljivko, zato se spremembe vidijo v vseh funkcijah, ki jo uporabljajo; drugi jeziki lahko zagotovijo kopijo vrednosti.
Uporabnost in kompromisi
- Uporabe: enkapsulacija (skriti podatki), tvorba generatorjev in števcem, funkcije višjega reda, delne aplikacije, dekoratorji, asinhroni povratni klici, itd.
- Kompromisi: zaprtja ohranjajo spremenljivke v pomnilniku, kar lahko vpliva na porabo pomnilnika; nepravilna raba lahko povzroči težave (npr. nepričakovane vrednosti pri zanki, če se zajemajo spremenljivke, ki se spreminjajo).
Zaključek
Zaprtja so močno orodje v programiranju, ki omogočajo, da funkcije prenesejo in ohranijo svoj kontekst. Razumevanje razlik med anonimnimi funkcijami in zaprtji, med leksikalnim in dinamičnim skopanjem ter specifičnim vedenjem posameznega programskega jezika je ključnega pomena za pravilno in učinkovito uporabo zaprtij.