Hva er en nedleggelse?

Nå og da ser jeg «lukkinger» blir nevnt, og jeg prøvde å slå opp, men Wiki gir ikke en forklaring som jeg forstår. Kan noen hjelp meg her?

Kommentarer

  • Hvis du vet Java / C # håper denne lenken vil hjelpe- http://www.developerfusion.com/article/8251/the-beauty-of-closures/
  • Stengninger er vanskelig å forstå. Du bør prøve å klikke på alle lenkene i første setning i den Wikipedia-artikkelen og forstå dem artikler først.
  • stackoverflow.com/questions/36636/what-is-a-closure
  • Hva ‘ er den grunnleggende forskjellen mellom en lukking og en klasse skjønt? Ok, en klasse med bare en offentlig metode.
  • @biziclop: Du kunne etterlign en lukking med en klasse (at ‘ er hva Java-devs må gjøre). Men de ‘ er vanligvis litt mindre ordrike å lage og du don t må håndtere manuelt det du ‘ gjør om. (Hardcore-lisperne stiller et lignende spørsmål, men kommer antagelig til den andre konklusjonen – at OO-støtte på språknivå er unødvendig når du har nedleggelser).

Svar

(Ansvarsfraskrivelse: dette er en grunnleggende forklaring; så langt som definisjonen går, forenkler jeg litt)

Den enkleste måten å tenke på en lukking er en -funksjon som kan lagres som en variabel (referert til som en «første -class-funksjon «), som har en spesiell mulighet til å få tilgang til andre variabler lokale for omfanget det ble opprettet i.

Eksempel (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();  

Funksjonene 1 tilordnet document.onclick og displayValOfBlack er nedleggelser. Du kan se at de begge refererer til den boolske variabelen black, men den variabelen er tildelt utenfor funksjonen. Fordi black er lokal for omfanget der funksjonen ble definert , bevares pekeren til denne variabelen.

Hvis du legger dette på en HTML-side:

  1. Klikk for å endre til svart
  2. Trykk [enter] for å se «true»
  3. Klikk igjen, skifter tilbake til hvitt
  4. Trykk [enter] for å se «false»

Dette viser at begge har tilgang til samme black, og kan brukes til å lagre tilstand uten noe innpakningsobjekt.

Anropet til setKeyPress er å demonstrere hvordan en funksjon kan overføres akkurat som enhver variabel. -omfanget som er bevart i nedleggelsen, er fortsatt den der funksjonen ble definert.

Stengninger er ofte brukt som hendelsesbehandler, spesielt i JavaScript og ActionScript. God bruk av nedleggelser hjelper deg med å implisitt binde variabler til hendelsesbehandlere uten å måtte lage en gjenstandsinnpakning. Uforsiktig bruk vil imidlertid føre til minnelekkasjer (for eksempel når en ubrukt, men bevart hendelsesbehandler er det eneste å holde på store gjenstander i minnet, spesielt DOM-objekter, som forhindrer søppeloppsamling).


1: Egentlig er alle funksjoner i JavaScript nedleggelser.

Kommentarer

  • Da jeg leste svaret ditt, kjente en lyspære tennes i tankene mine. Høyt verdsatt! 🙂
  • Siden black er erklært inne i en funksjon, ville ikke ‘ t bli ødelagt etter hvert som stakken slapper av. ..?
  • @gablin, det er det som er unikt med språk som har nedleggelser. Alle språk med søppeloppsamling fungerer på samme måte – når det ikke holdes flere referanser til et objekt, kan det bli ødelagt. Hver gang en funksjon blir opprettet i JS, er det lokale omfanget bundet til den funksjonen til den funksjonen er ødelagt.
  • @gablin, det ‘ er et godt spørsmål. Jeg tror ikke ‘ de kan ‘ t & mdash; men jeg tok bare opp søppelinnsamling siden det JS bruker og at ‘ er det du så ut til å henvise til da du sa » Siden black er erklært i en funksjon, ville ikke ‘ t som blir ødelagt «. Husk også at hvis du erklærer et objekt i en funksjon og deretter tilordner det til en variabel som lever et annet sted, blir objektet bevart fordi det er andre referanser til det.
  • Objective-C (og C under clang) støtter blokker, som i det vesentlige er lukkinger, uten søppeloppsamling. Det krever kjøretidsstøtte og noe manuell intervensjon rundt minneadministrasjon.

Svar

En lukking er i utgangspunktet bare en annen måte å se på et objekt. Et objekt er data som har en eller flere funksjoner knyttet til seg. En lukking er en funksjon som har en eller flere variabler knyttet til seg. De to er i utgangspunktet identiske, på et implementeringsnivå i det minste. Den virkelige forskjellen er i hvor de kommer fra.

I objektorientert programmering erklærer du en objektklasse ved å definere medlemsvariablene og metodene (medlemsfunksjoner) opp foran, og deretter oppretter du forekomster av den klassen. Hver forekomst kommer med en kopi av medlemsdataene, initialisert av konstruktøren. Du har da en variabel av en objekttype, og sender den rundt som et datastykke, fordi fokuset er på dens natur som data.

I en lukking, derimot, er ikke objektet definert foran som en objektklasse, eller instantiert gjennom en konstruktøranrop i koden din. I stedet skriver du nedleggelsen som en funksjon inne i en annen funksjon. Lukkingen kan referere til hvilken som helst av de ytre funksjonens lokale variabler, og kompilatoren oppdager det og flytter disse variablene fra den ytre funksjonens stabelplass til lukkets erklæring om skjult objekt. , og selv om det i utgangspunktet er et objekt under panseret, sender du det rundt som en funksjonsreferanse, fordi fokuset er på dets natur som en funksjon.

Kommentarer

  • +1: Godt svar. Du kan se en lukking som et objekt med bare en metode, og et vilkårlig objekt som en samling lukkinger over noen vanlige underliggende data (objektet ‘ medlemsvariabler). Jeg synes disse to synspunktene er ganske symmetriske.
  • Veldig bra svar. Det forklarer faktisk innsikten i nedleggelse.
  • @Mason Wheeler: Hvor lagres nedleggelsesdata? I stack som en funksjon? Eller i bunken som et objekt?
  • @RoboAlex: I bunken, fordi det ‘ er et objekt som ser ut som en funksjon .
  • @RoboAlex: Hvor en lukking og dens fangede data lagres, avhenger av implementeringen. I C ++ kan den lagres i bunken eller på bunken.

Svar

Begrepet nedleggelse kommer av det faktum at et stykke kode (blokk, funksjon) kan ha gratis variabler som er lukket (dvs. bundet til en verdi) av miljøet der kodeblokken er definert.

Ta for eksempel definisjonen av Scala-funksjonen :

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

I funksjonslegemet er det to navn (variabler) v og k som indikerer to heltallverdier. Navnet v er bundet fordi det er erklært som et argument for funksjonen addConstant (ved å se på funksjonserklæringen vet vi at v tildeles en verdi når funksjonen påkalles). Navnet k er gratis med funksjonen addConstant fordi funksjonen ikke inneholder noen anelse om hvilken verdi k er bundet til (og hvordan).

For å evaluere et anrop som:

val n = addConstant(10) 

må vi tilordne k en verdi, som bare kan skje hvis navnet k er definert i den konteksten der addConstant er definert. For eksempel:

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

Nå som vi har definert addConstant i en sammenheng der k er definert, addConstant har blitt en nedleggelse fordi alle dens frie variabler er nå lukket (bundet til en verdi): addConstant kan påberopes og sendes rundt som om det var en funksjon. Merk at den frie variabelen k er bundet til en verdi når lukkingen er definert , mens argumentvariabelen v er bundet når lukkingen er påkalt .

Så en lukking er i utgangspunktet en funksjon eller kodeblokk som kan få tilgang til ikke-lokale verdier gjennom sine gratisvariabler etter at disse er bundet av konteksten.

På mange språk, hvis du bruk en lukking bare når du kan gjøre det anonym , f.eks.

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

Merk at en funksjon uten frie variabler er et spesielt tilfelle av en lukking (med et tomt sett med gratis variabler). Analogt er en anonym funksjon et spesielt tilfelle av en anonym lukking , dvs. en anonym funksjon er en anonym lukking uten gratis variabler.

Kommentarer

  • Dette jibes godt med lukkede og åpne formler i logikken. Takk for svaret.
  • @RainDoctor: Gratis variabler er definert i logiske formler og i lambda-kalkulatoruttrykk på en lignende måte: lambda i et lambdauttrykk fungerer som en kvantifier i logiske formler med gratis / bundne variabler .

Svar

En enkel forklaring i 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) vil bruke den tidligere opprettede verdien av closure. Det returnerte alertValue -funksjonens navnområde blir koblet til det navneområdet der closure -variabelen ligger. Når du sletter hele funksjonen, vil verdien av closure -variabelen vil bli slettet, men inntil da vil alertValue -funksjonen alltid kunne lese / skrive verdien til variabelen closure.

Hvis du kjører denne koden, vil den første iterasjonen tildele verdien 0 til closure -variabelen. skriv om funksjonen til:

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

Og fordi alertValue trenger den lokale variabelen closure for å utføre funksjonen, binder den seg med verdien av den tidligere tildelte lokale variabelen closure.

Og nå hver gang du ringer til closure_example -funksjon, den vil skrive ut den økte verdien av closure -variabelen fordi alert(closure) bundet.

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

Kommentarer

  • takk, jeg gjorde ikke ‘ t test koden =) alt virker i orden nå.

Svar

En «lukking» er , i hovedsak, noen lokale stater og noen koder, kombinert til en pakke. Vanligvis kommer den lokale staten fra et omgivende (leksikalt) omfang, og koden er (i det vesentlige) en indre funksjon som deretter returneres til utsiden. Avslutningen er da en kombinasjon av de fangede variablene som den indre funksjonen ser og koden til den indre funksjonen.

Det er en av de tingene som dessverre er litt vanskelig å forklare pga. å være ukjent.

En analogi jeg med suksess brukte tidligere var «forestill deg at vi har noe vi kaller» boken «, i stengingen,» boka «er den kopien der, over i hjørnet , av TAOCP, men på bordlukningen er det den kopien av en Dresden Files-bok. Så avhengig av hvilken lukking du er i, resulterer koden «gi meg boken» i at forskjellige ting skjer. «

Kommentarer

  • Du glemte dette: no.wikipedia.org/wiki/Closure_(computer_programming) i svaret ditt.
  • Nei, jeg valgte bevisst å ikke lukke den siden.
  • » Tilstand og funksjon. «: Kan en C-funksjon med en static lokal variabel betraktes som en lukking? Gjør lukkinger i Haskell involverer tilstand?
  • @Giorgio Closures in Haskell lukker (tror jeg) over argumentene i det leksikale omfanget de ‘ er definert i, så, jeg ‘ d si » ja » (selv om jeg i beste fall ikke er kjent med Haskell). AC-funksjon med en statisk variabel er i beste fall en veldig begrenset lukking (du vil virkelig kunne opprette flere lukkinger fra en enkelt funksjon, med en static lokal variabel, du har nøyaktig en).
  • Jeg stilte dette spørsmålet med vilje fordi jeg tror at en C-funksjon med en statisk variabel ikke er en lukking: den statiske variabelen er definert lokalt og kun kjent inne i lukkingen, den får ikke tilgang miljøet. Jeg er heller ikke 100% sikker, men jeg vil formulere uttalelsen din omvendt: du bruker lukkemekanismen for å lage forskjellige funksjoner (en funksjon er en lukkedefinisjon + en binding for sine gratis variabler).

Svar

Det er vanskelig å definere hva lukking er uten å definere begrepet «tilstand».

I utgangspunktet , på et språk med full leksikalt omfang som behandler funksjoner som førsteklasses verdier, skjer det noe spesielt. Hvis jeg skulle gjøre noe sånt som:

function foo(x) return x end x = foo 

Variabelen x refererer ikke bare til function foo() men det refererer også til staten foo var igjen sist den returnerte. Den virkelige magien skjer når foo har andre funksjoner nærmere definert innenfor sitt omfang; det er som sitt eget mini-miljø (akkurat som «normalt» definerer vi funksjoner i et globalt miljø).

Funksjonelt kan det løse mange av de samme problemene som C ++ (C?) «s» statisk «nøkkelord, som beholder en lokal variabels tilstand gjennom flere funksjonsanrop; imidlertid er det mer som å bruke det samme prinsippet (statisk variabel) på en funksjon, ettersom funksjoner er førsteklasses verdier. lukking legger til støtte for hele funksjonens tilstand som skal lagres (ingenting å gjøre med C ++ s statiske funksjoner).

Å behandle funksjoner som førsteklasses verdier og legge til støtte for nedleggelser betyr også at du kan ha mer enn en forekomst av den samme funksjonen i minnet (ligner på klasser). Dette betyr at du kan bruke samme kode uten å måtte tilbakestille funksjonens tilstand, slik det er nødvendig når man arbeider med C ++ statiske variabler inne i en funksjon (kan være feil om dette?).

Her er noen tester av luas nedleggingsstøtte .

--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 

resultater:

nil 20 31 42 

Det kan bli vanskelig, og det varierer sannsynligvis fra språk til språk, men det virker i Lua at når en funksjon utføres, tilbakestilles tilstanden. Jeg sier dette fordi resultatene fra koden ovenfor ville være forskjellige hvis vi hadde tilgang til funksjon / tilstand direkte (i stedet for gjennom den anonyme funksjonen den returnerer), da pvalue vil bli tilbakestilt tilbake til 10; men hvis vi får tilgang til tilstanden min gjennom x (den anonyme funksjonen), kan du se at pvalue er levende og godt et sted i minnet. Jeg mistenker at det er litt mer til det, kanskje noen kan bedre forklare arten av implementeringen.

PS: Jeg vet ikke en slikk av C ++ 11 (annet enn hva som er i tidligere versjoner), så vær oppmerksom på at dette ikke er en sammenligning mellom nedleggelser i C ++ 11 og Lua. Også alle «linjene tegnet» fra Lua til C ++ er likheter som statiske variabler og lukkinger er ikke 100% de samme; selv om de noen ganger brukes til å løse lignende problemer.

Det jeg ikke er sikker på er i kodeeksemplet ovenfor om den anonyme funksjonen eller den høyere ordensfunksjonen regnes som nedleggelsen?

Svar

En lukking er en funksjon som har tilknyttet tilstand:

I perl oppretter du lukkinger slik:

#!/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 

Hvis vi ser på den nye funksjonaliteten som følger med C ++.
Det lar deg også binde nåværende tilstand til objektet:

#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"); } 

Svar

La oss vurdere en enkel funksjon:

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

Denne funksjonen kalles en toppnivåfunksjon fordi den ikke er nestet i noen annen funksjon. Hver JavaScript-funksjonen knytter en liste over objekter som kalles en » Omfangskjede «. Denne omfangskjeden er en ordnet liste over gjenstander ach av disse objektene definerer noen variabler.

I funksjoner på toppnivå består omfangskjeden av et enkelt objekt, det globale objektet. For eksempel har funksjonen f1 ovenfor en omfangskjede som har et enkelt objekt i seg som definerer alle de globale variablene. (merk at ordet «objekt» her ikke betyr JavaScript-objekt, det er bare et implementeringsdefinert objekt som fungerer som en variabel beholder, der JavaScript kan «slå opp» variabler.)

Når dette funksjon blir påkalt, JavaScript oppretter noe som heter «Aktiveringsobjekt» , og setter det øverst i omfangskjeden. Dette objektet inneholder alle de lokale variablene (for eksempel x her). Derfor har vi nå to objekter i omfangskjeden: det første er aktiveringsobjektet og under det er det globale objektet. / p>

Vær veldig nøye med at de to objektene settes inn i omfangskjeden til ULIKE tider. Det globale objektet settes når funksjonen er definert (dvs. når JavaScript analyserte funksjonen og opprettet funksjonsobjektet), og aktiveringsobjekt kommer inn når funksjonen påkalles.

Så, vi vet dette nå:

  • Hver funksjon har en omfangskjede tilknyttet
  • Nårfunksjonen er definert (når funksjonsobjektet er opprettet), lagrer JavaScript en omfangskjede med den funksjonen
  • For toppnivåfunksjoner inneholder omfangskjeden bare det globale objektet på funksjonsdefinisjonstidspunktet og legger til en ekstra aktiveringsobjekt på toppen ved innkallingstid

Situasjonen blir interessant når vi håndterer nestede funksjoner. Så la oss opprette en:

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

Når f1 blir definert, får vi en omfangskjede for den som inneholder bare det globale objektet.

Nå når f1 blir ringt, får omfangskjeden til f1 aktiveringsobjektet. Dette aktiveringsobjektet inneholder variabelen x og variabelen f2 som er en funksjon. Og vær oppmerksom på at f2 blir definert.Derfor lagrer JavaScript på dette tidspunktet også en ny omfangskjede for f2. Omfangskjeden som er lagret for denne indre funksjonen er den gjeldende omfangskjeden i kraft. Det nåværende omfanget kjede i kraft er den av f1 «s. Derfor er f2» s omfangskjede f1 «s nåværende omfangskjede – som inneholder aktiveringsobjektet til f1 og det globale objektet.

Når f2 kalles, får det «s eget aktiveringsobjekt som inneholder y, lagt til omfangskjeden som allerede inneholder aktiveringsobjektet til f1 og det globale objektet.

Hvis det var en annen nestet funksjon definert i f2, dens omfangskjede vil inneholde tre objekter på definisjonstidspunktet (2 aktiveringsobjekter med to ytre funksjoner, og det globale objektet), og 4 på innkallingstiden.

nå undersøker vi Tand hvordan omfangskjede fungerer, men vi har ikke snakket om nedleggelser ennå.

Kombinasjonen av et funksjonsobjekt og et omfang (et sett med variable bindinger) der funksjonens variabler løses, kalles en nedleggelse i datalogisk litteratur – JavaScript den definitive guiden av David Flanagan

De fleste funksjoner påkalles ved hjelp av samme omfangskjede som var i kraft da funksjonen ble definert, og det spiller ingen rolle at det er en lukking involvert. Stengninger blir interessante når de påberopes under en annen omfangskjede enn den som var i kraft da de ble definert. Dette skjer oftest når et nestet funksjonsobjekt returneres fra funksjonen det ble definert i.

Når funksjonen kommer tilbake, fjernes det aktiveringsobjektet fra omfangskjeden. Hvis det ikke var noen nestede funksjoner, er det ikke flere referanser til aktiveringsobjektet, og det blir søppel samlet. Hvis det var definert nestede funksjoner, har hver av disse funksjonene en referanse til omfangskjeden, og den omfangskjeden refererer til aktiveringsobjektet.

Hvis de nestede funksjonsobjektene forblir innenfor deres ytre funksjon, da vil de selv bli søppeloppsamlet, sammen med aktiveringsobjektet de refererte til. Men hvis funksjonen definerer en nestet funksjon og returnerer den eller lagrer den i en eiendom et sted, så vil det være en ekstern referanse til den nestede funksjonen. Det blir ikke samlet søppel, og aktiveringsobjektet det refererer til, blir heller ikke søppel.

I eksemplet vårt ovenfor returnerer vi ikke f2 fra f1, derav, når et anrop til f1 returneres, blir aktiveringsobjektet fjernet fra omfangskjeden og søppel samlet. Men hvis vi hadde noe sånt som dette:

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

Her vil den returnerende f2 ha en omfangskjede som vil inneholder aktiveringsobjektet til f1, og det vil derfor ikke samles inn søppel. På dette punktet, hvis vi kaller f2, vil den kunne få tilgang til f1 «s variabel x selv om vi «er ute av f1.

Derfor kan vi se at en funksjon holder sin omfangskjede med seg og med omfangskjeden kommer alle aktiveringsobjektene til ytre funksjoner. Dette er essensen av lukking. Vi sier at funksjoner i JavaScript er «leksikalt omfang» , noe som betyr at de lagrer omfanget som var aktivt da de ble definert i motsetning til omfanget som var aktivt da de ringte.

Det finnes en rekke kraftige programmeringsteknikker som involverer nedleggelser som tilnærming til private variabler. , hendelsesdrevet programmering, delvis applikasjon osv.

Vær også oppmerksom på at alt dette gjelder alle de språkene som støtter nedleggelser. PHP (5.3+), Python, Ruby, etc.

Svar

En lukking er en kompilatoroptimalisering (aka syntaktisk sukker?). Noen mennesker har også referert til dette som Poor Mans Object .

Se svaret fra Eric Lippert : (utdrag nedenfor)

Kompilatoren genererer kode slik:

 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; }  

Fornuftig?
Du ba også om sammenligninger. VB og JScript skaper begge nedleggelser på omtrent samme måte.

Kommentarer

  • Dette svaret er en CW fordi jeg ikke ‘ t fortjener poeng for Eric ‘ s flott svar.Vennligst oppstem det slik du ønsker det. HTH
  • -1: Forklaringen din er for rot i C #. Lukking brukes på mange språk og er mye mer enn syntaktisk sukker på disse språkene og omfatter både funksjonen og tilstanden.
  • Nei, en lukking er verken bare en » kompilatoroptimalisering » eller syntaktisk sukker. -1

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *