Ce este o închidere?

Din când în când văd „închideri” menționate și am încercat să-l caut, dar Wiki nu oferă o explicație pe care o înțeleg. Ar putea cineva ajută-mă aici?

Comentarii

  • Dacă știți Java / C # sper că acest link vă va ajuta- http://www.developerfusion.com/article/8251/the-beauty-of-closures/
  • Închiderile sunt greu de înțeles. Ar trebui să încercați să faceți clic pe toate linkurile din prima teză a acelui articol Wikipedia și să le înțelegeți articolele mai întâi.
  • stackoverflow.com/questions/36636/what-is-a-closure
  • Ce ‘ este diferența fundamentală dintre o închidere și o clasă? Bine, o clasă cu o singură metodă publică.
  • @biziclop: ai putea emulez o închidere cu o clasă (că ‘ este ceea ce trebuie să facă Java dev). Dar ‘ sunt de obicei puțin mai detaliate pentru a crea și nu nu trebuie să gestionați manual ceea ce ‘ faceți. (Lispers-urile puternice pun o întrebare similară, dar probabil ajung la acea altă concluzie – că suportul OO la nivel de limbă nu este necesar atunci când aveți închideri).

Răspuns

(Disclaimer: aceasta este o explicație de bază; în ceea ce privește definiția, simplific puțin)

Cel mai simplu mod de a vă gândi la o închidere este o funcție care poate fi stocată ca o variabilă (denumită „prima” -class function „), care are o abilitate specială de a accesa alte variabile locale în domeniul în care a fost creat.

Exemplu (JavaScript):

 var setKeyPress = function(callback) { document.onkeypress = callback; }; var initialize = function() { var black = false; document.onclick = function() { black = !black; document.body.style.backgroundColor = black ? "#000000" : "transparent"; } var displayValOfBlack = function() { alert(black); } setKeyPress(displayValOfBlack); }; initialize();  

Funcțiile 1 atribuite document.onclick și displayValOfBlack sunt închideri. Puteți vedea că ambele fac referire la variabila booleană black, dar variabila respectivă este atribuită în afara funcției. Deoarece black este local pentru domeniul în care a fost definită funcția , indicatorul către această variabilă este păstrat.

Dacă puneți acest lucru într-o pagină HTML:

  1. Faceți clic pentru a trece la negru
  2. Apăsați [enter] pentru a vedea „adevărat”
  3. Faceți clic din nou, revine la alb
  4. Apăsați [enter] pentru a vedea „fals”

Aceasta demonstrează că ambele au acces la același black, și poate fi folosit pentru a stoca starea fără niciun obiect wrapper.

Apelul către setKeyPress este pentru a demonstra cum poate fi transmisă o funcție la fel ca orice variabilă. domeniul păstrat în închidere este încă cel în care funcția a fost definită.

Închiderile sunt de obicei folosit ca gestionare de evenimente, în special în JavaScript și ActionScript. O utilizare bună a închiderilor vă va ajuta să legați implicit variabilele la gestionarele de evenimente, fără a fi nevoie să creați un wrapper de obiecte. Cu toate acestea, utilizarea neglijentă va duce la scurgeri de memorie (cum ar fi atunci când un gestionar de evenimente neutilizat, dar conservat este singurul lucru pe care trebuie să-l țineți de obiectele mari din memorie, în special obiectele DOM, prevenind colectarea gunoiului). > 1: De fapt, toate funcțiile din JavaScript sunt închideri.

Comentarii

  • În timp ce citeam răspunsul dvs., am am simțit aprinderea unui bec în mintea mea. Foarte apreciat! 🙂
  • Deoarece black este declarat în interiorul unei funcții, ‘ nu ar fi distruse pe măsură ce stiva se derulează. ..?
  • @gablin, acesta este ceea ce este unic în limbile cu închideri. Toate limbile cu colectarea gunoiului funcționează în același mod – atunci când nu mai există referințe la un obiect, acesta poate fi distrus. Ori de câte ori este creată o funcție în JS, domeniul local este legat de acea funcție până când funcția respectivă este distrusă.
  • @gablin, ‘ este o întrebare bună. Nu ‘ nu cred că pot ‘ t & mdash; dar am adus colectarea gunoiului doar de la ceea ce folosește JS și de la ‘ la ceea ce păreai că te referi când ai spus ” black este declarat în interiorul unei funcții, nu ‘ ar fi distrus „. Amintiți-vă, de asemenea, că dacă declarați un obiect într-o funcție și apoi îl atribuiți unei variabile care trăiește în altă parte, acel obiect este păstrat deoarece există alte referințe la acesta.
  • Obiectiv-C (și C sub clang) suportă blocuri, care sunt în esență închideri, fără colectarea gunoiului. Necesită suport pentru runtime și o intervenție manuală în jurul gestionării memoriei.

Răspuns

O închidere este în esență doar un mod diferit de a privi un obiect. Un obiect reprezintă date care au una sau mai multe funcții legate de acesta. O închidere este o funcție care are legată una sau mai multe variabile. Cele două sunt practic identice, cel puțin la un nivel de implementare. Adevărata diferență constă în proveniența lor.

În programarea orientată pe obiecte, declarați o clasă de obiecte definindu-le variabilele de membru și metodele sale (funcțiile de membru) în față, apoi creați instanțe de acea clasă. Fiecare instanță vine cu o copie a datelor membrilor, inițializată de constructor. Apoi aveți o variabilă de tip obiect și o transmiteți ca o bucată de date, deoarece accentul este pus pe natura sa ca date.

Pe de altă parte, într-o închidere, obiectul nu este definite în avans ca o clasă de obiecte sau instanțiate printr-un apel constructor în codul dvs. În schimb, scrieți închiderea ca funcție în interiorul altei funcții. Închiderea se poate referi la oricare dintre variabilele locale ale funcției externe, iar compilatorul detectează și mută aceste variabile din spațiul stivei funcției externe în declarația obiectului ascuns al închiderii. Aveți apoi o variabilă de tip închidere , și chiar dacă este practic un obiect sub capotă, îl treceți ca referință de funcție, deoarece accentul este pus pe natura sa ca funcție.

Comentarii

  • +1: răspuns bun. Puteți vedea o închidere ca un obiect cu o singură metodă și un obiect arbitrar ca o colecție de închideri asupra unor date subiacente comune (variabilele membre ale obiectului ‘). Cred că aceste două puncte de vedere sunt destul de simetrice.
  • Răspuns foarte bun. De fapt, explică ideea închiderii.
  • @ Mason Wheeler: Unde sunt stocate datele de închidere? În stivă ca o funcție? Sau în heap ca un obiect?
  • @RoboAlex: În heap, deoarece ‘ este un obiect care arată o funcție .
  • @RoboAlex: Unde sunt stocate o închidere și datele capturate ale acesteia depinde de implementare. În C ++ poate fi stocat în heap sau în stivă.

Răspuns

Termenul închidere provine din faptul că o bucată de cod (bloc, funcție) poate avea variabile libere care sunt închis (adică legat de o valoare) de mediul în care este definit blocul de cod.

Luați de exemplu definiția funcției Scala :

def addConstant(v: Int): Int = v + k 

În corpul funcției există două nume (variabile) v și k indicând două valori întregi. Numele v este legat deoarece este declarat ca argument al funcției addConstant (uitându-ne la declarația funcției știm că v i se va atribui o valoare atunci când funcția este invocată). Numele k este gratuit cu funcția addConstant deoarece funcția nu conține nicio idee despre ce valoare k este legat de (și cum).

Pentru a evalua un apel precum:

val n = addConstant(10) 

trebuie să atribuim k o valoare, care se poate întâmpla numai dacă numele k este definit în contextul în care addConstant este definit. De exemplu:

def increaseAll(values: List[Int]): List[Int] = { val k = 2 def addConstant(v: Int): Int = v + k values.map(addConstant) } 

Acum că am definit addConstant într-un context în care k este definit, addConstant a devenit o închidere deoarece toate variabilele sale libere sunt acum închise (legate de o valoare): addConstant fi invocat și trecut ca și cum ar fi o funcție. Rețineți că variabila gratuită k este legată de o valoare când închiderea este definită , întrucât variabila argument v este legată atunci când închiderea este invocată .

Deci, o închidere este practic o funcție sau un bloc de cod care poate accesa valorile nelocale prin variabilele sale libere după ce acestea au fost legate de context.

În multe limbi, dacă utilizați o închidere numai odată ce o puteți face anonimă , de ex.

def increaseAll(values: List[Int]): List[Int] = { val k = 2 values.map(v => v + k) } 

Rețineți că o funcție fără variabile libere este un caz special de închidere (cu un set gol de variabile libere). În mod similar, o funcție anonimă este un caz special de închidere anonimă div id = „aeb5abc95a”>

, adică o funcție anonimă este o închidere anonimă fără variabile libere.

Comentarii

  • Acest lucru este bun cu formulele închise și deschise în logică. Vă mulțumim pentru răspunsul dvs. .

Răspuns

O explicație simplă în JavaScript:

var closure_example = function() { var closure = 0; // after first iteration the value will not be erased from the memory // because it is bound with the returned alertValue function. return { alertValue : function() { closure++; alert(closure); } }; }; closure_example(); 

alert(closure) va utiliza valoarea creată anterior de closure. Spațiul de nume al funcției alertValue returnat va fi conectat la spațiul de nume în care se află variabila closure. Când ștergeți întreaga funcție, valoarea variabilei closure va fi ștearsă, dar până atunci, funcția alertValue va putea întotdeauna să citească / scrie valoarea variabilei closure.

Dacă rulați acest cod, prima iterație va atribui o valoare 0 variabilei closure și rescrieți funcția în:

var closure_example = function(){ alertValue : function(){ closure++; alert(closure); } } 

Și pentru că alertValue are nevoie de variabila locală closure pentru a executa funcția, se leagă de valoarea variabilei locale atribuite anterior closure.

Și acum de fiecare dată când apelați closure_example, va scrie valoarea incrementată a variabilei closure deoarece alert(closure) este legat.

closure_example.alertValue()//alerts value 1 closure_example.alertValue()//alerts value 2 closure_example.alertValue()//alerts value 3 //etc. 

Comentarii

  • mulțumesc, nu ‘ t testați codul =) totul pare bine acum.

Răspuns

O „închidere” este , în esență, unele state locale și unele coduri, combinate într-un pachet. De obicei, statul local provine dintr-un domeniu (lexical) înconjurător și codul este (în esență) o funcție interioară care este apoi returnată în exterior. Închiderea este apoi o combinație a variabilelor capturate pe care le vede funcția interioară și codul funcției interioare.

Este unul dintre acele lucruri care, din păcate, este puțin greu de explicat, datorită fiind necunoscut.

O analogie pe care am folosit-o cu succes în trecut a fost „imaginați-vă că avem ceva ce numim„ cartea ”, în camera de închidere,„ cartea ”este exemplarul de acolo, în colț , de la TAOCP, dar pe tabelul de închidere, este „copia acelei cărți Dresden Files. Deci, în funcție de închiderea în care te afli, codul„ dă-mi cartea „are ca rezultat diferite lucruri.”

Comentarii

  • Ați uitat acest lucru: en.wikipedia.org/wiki/Closure_(computer_programming) în răspunsul dvs.
  • Nu, am ales în mod conștient să nu închid pagina respectivă.
  • ” Stare și funcție. „: Poate fi considerată o închidere o funcție C cu o variabilă static locală? în Haskell implică starea?
  • @Giorgio Closures în Haskell închid (cred) argumentele din sfera lexicală în care ‘ sunt definite, așa că eu ‘ spune ” da ” (deși în cel mai bun caz nu sunt familiarizat cu Haskell). Funcția CA cu o variabilă statică este, în cel mai bun caz, o închidere foarte limitată (chiar doriți să puteți crea închideri multiple dintr-o singură funcție, cu o variabilă locală static, aveți exact una).
  • Am pus această întrebare intenționat, deoarece cred că o funcție C cu o variabilă statică nu este o închidere: variabila statică este definită local și este cunoscută doar în interiorul închiderii, nu accesează mediul. De asemenea, nu sunt 100% sigur, dar aș formula afirmația dvs. invers: utilizați mecanismul de închidere pentru a crea diferite funcții (o funcție este o definiție de închidere + o legare pentru variabilele sale libere).

Răspuns

Este greu de definit ce este închiderea fără a defini conceptul de „stat”.

Practic , într-un limbaj cu scop lexical complet care tratează funcțiile ca valori de primă clasă, se întâmplă ceva special. Dacă ar fi să fac ceva de genul:

function foo(x) return x end x = foo 

Variabila x nu numai referințe function foo(), dar face trimitere și la starea foo a fost lăsată ultima dată când a revenit. Adevărata magie se întâmplă atunci când foo are alte funcții definite în continuare în sfera sa; „seamănă cu propriul său mini-mediu (la fel de„ în mod normal ”definim funcțiile într-un mediu global).

Funcțional poate rezolva multe din aceleași probleme ca și C ++ (C?) Cuvânt cheie „s” static ”, care păstrează starea unei variabile locale pe mai multe apeluri de funcții; cu toate acestea, este mai mult ca aplicarea aceluiași principiu (variabilă statică) unei funcții, deoarece funcțiile sunt valori de primă clasă; închiderea adaugă suport pentru întreaga stare a funcției care trebuie salvată (nimic nu are legătură cu funcțiile statice ale C ++).

Tratarea funcțiilor ca valori de primă clasă și adăugarea de suport pentru închideri înseamnă, de asemenea, că puteți avea mai multe instanțe ale aceleiași funcții în memorie (asemănătoare claselor). Ceea ce înseamnă acest lucru este că puteți reutiliza același cod fără a fi nevoie să resetați starea funcției, așa cum este necesar atunci când se tratează variabile statice C ++ din interiorul unei funcții (poate fi greșit în acest sens?).

Iată câteva teste ale suportului de închidere al lui Lua .

--Closure testing --By Trae Barlow -- function myclosure() print(pvalue)--nil local pvalue = pvalue or 10 return function() pvalue = pvalue + 10 --20, 31, 42, 53(53 never printed) print(pvalue) pvalue = pvalue + 1 --21, 32, 43(pvalue state saved through multiple calls) return pvalue end end x = myclosure() --x now references anonymous function inside myclosure() x()--nil, 20 x() --21, 31 x() --32, 42 --43, 53 -- if we iterated x() again 

rezultate:

nil 20 31 42 

Poate fi dificil și probabil variază de la limbă la limbă, dar în Lua se pare că ori de câte ori se execută o funcție, starea acesteia este resetată. Spun asta deoarece rezultatele din codul de mai sus ar fi diferite dacă am accesa funcție / stare direct (în loc să revină prin funcția anonimă), deoarece pvalue ar fi resetat la 10; dar dacă accesăm starea myclosure prin x (funcția anonimă), puteți vedea că pvalue este viu și undeva în memorie. Bănuiesc că există ceva mai mult, probabil cineva poate explica mai bine natura implementării.

PS: Nu știu o lingură de C ++ 11 (în afară de ceea ce este în versiunile anterioare), așa că rețineți că nu este o comparație între închiderile în C ++ 11 și Lua. De asemenea, toate „liniile trasate” de la Lua la C ++ sunt asemănări ca variabile statice și închiderile nu sunt 100% la fel; chiar dacă uneori sunt folosite pentru a rezolva probleme similare.

Lucrul despre care nu sunt sigur este că, în exemplul de cod de mai sus, dacă funcția anonimă sau funcția de ordin superior este considerată închidere?

Răspuns

O închidere este o funcție care are o stare asociată:

În perl creați închideri ca aceasta:

#!/usr/bin/perl # This function creates a closure. sub getHelloPrint { # Bind state for the function we are returning. my ($first) = @_;a # The function returned will have access to the variable $first return sub { my ($second) = @_; print "$first $second\n"; }; } my $hw = getHelloPrint("Hello"); my $gw = getHelloPrint("Goodby"); &$hw("World"); // Print Hello World &$gw("World"); // PRint Goodby World 

Dacă ne uităm la noua funcționalitate furnizată cu C ++.
De asemenea, vă permite să legați starea curentă la obiect:

#include <string> #include <iostream> #include <functional> std::function<void(std::string const&)> getLambda(std::string const& first) { // Here we bind `first` to the function // The second parameter will be passed when we call the function return [first](std::string const& second) -> void { std::cout << first << " " << second << "\n"; }; } int main(int argc, char* argv[]) { auto hw = getLambda("Hello"); auto gw = getLambda("GoodBye"); hw("World"); gw("World"); } 

Răspuns

Să considerăm o funcție simplă:

function f1(x) { // ... something } 

Această funcție se numește funcție de nivel superior deoarece nu este imbricată în nici o altă funcție. Fiecare Funcția JavaScript asociază cu ea însăși o listă de obiecte numite ” Scope Chain „. Acest lanț de scop este o listă ordonată de obiecte .. E ach a acestor obiecte definește unele variabile.

În funcțiile de nivel superior, lanțul de scopuri constă dintr-un singur obiect, obiectul global. De exemplu, funcția f1 de mai sus are un lanț de domeniu care conține un singur obiect care definește toate variabilele globale. (rețineți că termenul „obiect” aici nu înseamnă obiect JavaScript, este doar un obiect definit de implementare care acționează ca un container variabil, în care JavaScript poate „căuta” variabile.)

Atunci când funcția este invocată, JavaScript creează ceva numit „Obiect de activare” și îl plasează în partea de sus a lanțului de scop. obiect conține toate variabilele locale (de exemplu x aici). Prin urmare, acum avem două obiecte în lanțul de scopuri: primul este obiectul de activare și dedesubt este obiectul global.

Rețineți cu mare atenție că cele două obiecte sunt plasate în lanțul de scopuri la DIFERITE ori. Obiectul global este pus atunci când funcția este definită (adică, când JavaScript a analizat funcția și a creat obiectul funcției), iar obiectul de activare intră atunci când funcția este invocată.

Deci, acum știm acest lucru:

  • Fiecare funcție are un lanț de domeniu asociat cu el
  • Cândfuncția este definită (când se creează obiectul funcției), JavaScript salvează un lanț de scopuri cu acea funcție
  • Pentru funcțiile de nivel superior, lanțul de scopuri conține doar obiectul global la momentul definirii funcției și adaugă un supliment obiect de activare în partea de sus la momentul invocării

Situația devine interesantă atunci când ne ocupăm de funcții imbricate. Deci, să creăm unul:

function f1(x) { function f2(y) { // ... something } } 

Când f1 este definit, obținem un lanț de scopuri pentru acesta numai obiectul global.

Acum, când este apelat f1, lanțul de scopuri al f1 primește obiectul de activare. Acest obiect de activare conține variabila x și variabila f2 care este o funcție. Și rețineți că f2 este definit.Prin urmare, în acest moment, JavaScript salvează, de asemenea, un nou lanț de domeniu pentru f2. Lanțul de domeniu salvat pentru această funcție interioară este lanțul de domeniu actual în vigoare. Domeniul de aplicare actual lanțul efectiv este cel al f1 „s. Prin urmare, lanțul de scop f2” este f1 „s curent lanț de domeniu – care conține obiectul de activare al f1 și obiectul global.

Când se apelează f2, acesta primește propriul obiect de activare care conține y, adăugat lanțului său de scop care conține deja obiectul de activare al f1 și obiectul global.

Dacă ar exista o altă funcție imbricată definită în f2, lanțul său de scop ar conține trei obiecte la momentul definirii (2 obiecte de activare a două funcții exterioare și obiectul global) și 4 la momentul invocării.

Deci, acum noi sub cum funcționează lanțul de domeniu, dar nu am vorbit încă despre închideri.

Combinația dintre un obiect funcțional și un domeniu (un set de legături variabile) în care variabilele funcției sunt rezolvate se numește închidere în literatura informatică – JavaScript ghidul definitiv de David Flanagan

Majoritatea funcțiilor sunt invocate utilizând același lanț de domeniu care era în vigoare la definirea funcției și nu contează cu adevărat că există o închidere implicată. Închiderile devin interesante atunci când sunt invocate sub un lanț de domeniu diferit de cel care era în vigoare când au fost definite. Acest lucru se întâmplă cel mai frecvent atunci când un obiect funcție imbricat este returnat din funcția în care a fost definit.

Când funcția revine, acel obiect de activare este eliminat din lanțul de scopuri. Dacă nu au existat funcții imbricate, nu mai există referințe la obiectul de activare și se colectează gunoiul. Dacă au fost definite funcții imbricate, atunci fiecare dintre aceste funcții are o referință la lanțul de scop și acel lanț de referință se referă la obiectul de activare.

Dacă acele obiecte de funcții imbricate au rămas în funcția lor externă, atunci ei înșiși vor fi colectați gunoi, împreună cu obiectul de activare la care s-au referit. Dar dacă funcția definește o funcție imbricată și o returnează sau o stochează undeva într-o proprietate, atunci va exista o referință externă la funcția imbricată. Nu va fi colectat gunoi și nici obiectul de activare la care se referă nu va fi colectat gunoi.

În exemplul nostru de mai sus, nu returnăm f2 din f1, prin urmare, când revine un apel către f1, obiectul său de activare va fi eliminat din lanțul de scop și colectarea gunoiului. Dar dacă am avea așa ceva:

function f1(x) { function f2(y) { // ... something } return f2; } 

Aici, f2 care va reveni va avea un lanț de scop care va avea conține obiectul de activare al f1 și, prin urmare, nu va fi colectat gunoi. În acest moment, dacă numim f2, acesta va putea accesa variabila f1 „s x chiar dacă „ieșim din f1.

Prin urmare, putem vedea că o funcție își păstrează lanțul de scopuri cu el și cu lanțul de scopuri vin toate obiectele de activare ale funcțiilor externe. Aceasta este esența închiderii. Spunem că funcțiile din JavaScript sunt „cu scop lexical” , ceea ce înseamnă că salvează domeniul de aplicare activ când au fost definite, spre deosebire de domeniul activ atunci când au fost chemați.

Există o serie de tehnici de programare puternice care implică închideri precum aproximarea variabilelor private , programare bazată pe evenimente, aplicație parțială etc.

De asemenea, rețineți că toate acestea se aplică tuturor acelor limbi care acceptă închideri. De exemplu, PHP (5.3+), Python, Ruby etc.

Răspuns

O închidere este o optimizare a compilatorului (cunoscut și ca zahăr sintactic?). Unii oameni s-au referit la acest lucru și ca obiectul săracului .

Vedeți răspunsul lui Eric Lippert : (extras de mai jos)

Compilatorul va genera cod astfel:

 private class Locals { public int count; public void Anonymous() { this.count++; } } public Action Counter() { Locals locals = new Locals(); locals.count = 0; Action counter = new Action(locals.Anonymous); return counter; }  

Are sens?
De asemenea, ați solicitat comparații. VB și JScript creează ambele închideri cam în același mod.

Comentarii

  • Acest răspuns este un CW, deoarece nu ‘ nu merit puncte pentru Eric ‘.Vă rugăm să votați-l după cum doriți. HTH
  • -1: explicația dvs. este prea rădăcină în C #. Închiderea este utilizată în multe limbi și este mult mai mult decât zahărul sintactic în aceste limbi și cuprinde atât funcția, cât și starea.
  • Nu, o închidere nu este nici doar un ” optimizare compilator ” nici zahăr sintactic. -1

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *