Koks skirtumas tarp „uždarymo“ ir „lambda“?

Ar kas nors gali paaiškinti? Suprantu pagrindines jų koncepcijas, bet dažnai pastebiu, kad jos naudojamos pakaitomis, ir aš jaučiuosi nepatogiai.

O dabar, kai esame čia, kaip jie skiriasi nuo įprastos funkcijos?

645
21 окт. set sker 21 spalis 2008-10-21 06:12 '08, 6:12 2008-10-21 06:12
@ 10 atsakymų

Lambda yra tik anoniminė funkcija - funkcija, apibrėžta be vardo. Kai kuriose kalbose, pvz., Schemoje, jos yra lygiavertės nurodytoms funkcijoms. Tiesą sakant, funkcijos apibrėžimas perrašomas kaip lambda prijungimas prie kintamojo. Kitose kalbose, pvz., „Python“, tarp jų yra (o ne nereikalingų) skirtumų, tačiau jie elgiasi taip pat skirtingai.

Uždarymas yra bet kokia funkcija, kuri uždaro aplinką , kurioje ji buvo apibrėžta. Tai reiškia, kad jis gali pasiekti kintamuosius, kurie nėra parametrų sąraše. Pavyzdžiai:

 def func(): return h def anotherfunc(h): return func() 

Tai sukels klaidą, nes func nekeičia aplinkos kitose anotherfunc . func uždaryta tik pasaulinėje aplinkoje. Tai veiks:

 def anotherfunc(h): def func(): return h return func() 

Kadangi čia func yra apibrėžta kitoje anotherfunc , o anotherfunc 2.3 ir naujesnėse (arba tam anotherfunc ), kai jie beveik gavo teisingus uždarymus (mutacija vis dar neveikia), tai reiškia, kad ji uždaro kitą anotherfunc ir gali pasiekti kintamuosius viduje. „Python 3.1+“ mutacija taip pat veikia, kai naudojamas nonlocal .

Kitas svarbus punktas - func ir toliau bus uždarytas trečiadienį, net jei jis nebebus vertinamas anotherfunc . Šis kodas taip pat veiks:

 def anotherfunc(h): def func(): return h return func print anotherfunc(10)() 

Tai atspausdins 10.

Tai, kaip pastebėjote, neturi nieko bendra su lambda s - tai dvi skirtingos (nors ir susijusios) sąvokos.

584
21 окт. Claudiu atsakė spalio 21 dieną 2008-10-21 06:58 '08, 06:58, 2008-10-21 06:58

Kai dauguma žmonių galvoja apie funkcijas, jie galvoja apie šias funkcijas :

 function foo() { return "This string is returned from the 'foo' function"; } 

Jie vadinami pavadinimu, žinoma:

 foo(); //returns the string above 

Naudodami lambda išraiškas, galite turėti anonimines funkcijas :

  @foo = lambda() {return "This is returned from a function without a name";} 

Pirmiau pateiktame pavyzdyje galite paskambinti lambda per kintamąjį, kurį jis priskyrė:

 foo(); 

Naudingesnė už anoniminių funkcijų priskyrimą kintamiesiems, perduoti juos aukštesniojo lygio funkcijoms arba iš jų, t. funkcijas, kurios priima / grąžina kitas funkcijas. Daugeliu atvejų funkcijos nereikalaujama:

 function filter(list, predicate) { @filteredList = []; for-each (@x in list) if (predicate(x)) filteredList.add(x); return filteredList; } //filter for even numbers filter([0,1,2,3,4,5,6], lambda(x) {return (x mod 2 == 0)}); 

Uždarymas gali būti įvardinta arba anoniminė funkcija, tačiau ji yra žinoma, kai ji „uždaro“ kintamuosius toje srityje, kurioje yra apibrėžta funkcija, t.y. uždarymas vis dar taikomas aplinkai su bet kuriais išoriniais kintamaisiais, kurie naudojami pačiame uždaryme. Čia yra nurodytas uždarymas:

 @x = 0; function incrementX() { x = x + 1;} incrementX(); // x now equals 1 

Tai neatrodo kaip daug, bet kas, jei visa tai yra kitoje funkcijoje, ir jūs perduodate išorinę funkciją į incrementX funkciją?

 function foo() { @x = 0; function incrementX() { x = x + 1; return x; } return incrementX; } @y = foo(); // y = closure of incrementX over foo.x y(); //returns 1 (yx == 0 + 1) y(); //returns 2 (yx == 1 + 1) 

Taigi, jūs gaunate objektus su valstybės išsaugojimu funkciniame programavime. Kadangi pavadinimas „incrementX“ nereikalingas, šiuo atveju galite naudoti lambda:

 function foo() { @x = 0; return lambda() { x = x + 1; return x; }; } 
161
21 окт. Mark Cidade atsakymas, spalio 21 d 2008-10-21 06:46 '08, 06:46, 2008-10-21 06:46

Yra daug painiavos dėl lambda ir uždarymo, net ir atsakant į šį StackOverflow klausimą. Užuot prašę atsitiktinių programuotojų, kurie išmoko uždaryti praktiką su tam tikromis programavimo kalbomis ar kitais nepažįstamais programuotojais, eikite į šaltinį (kur viskas prasidėjo). Ir kadangi lambda ir vartai yra kilę iš „ Lambda Calculus“ , kurį Alonso bažnyčia sugalvojo 30-ajame dešimtmetyje, kol pasirodė pirmieji elektroniniai kompiuteriai, tai yra šaltinis, apie kurį kalbu.

Lambda Calculus yra lengviausia programavimo kalba pasaulyje. Vienintelis dalykas, kurį galite padaryti:

  • TAIKYMAS: vienos išraiškos taikymas kitam, žymimas fx .
    (pagalvokite kaip funkciją, kur f yra funkcija ir x yra vienintelis parametras)
  • „ABSTRACTION“: susieja simbolį, kuris atsiranda išraiška, nurodydamas, kad šis simbolis yra tik „lizdas“, tuščias laukas, laukiantis užpildyti vertę, kaip „kintamasis“. Tai daroma pridedant graikų raidę λ (lambda), tada simbolinį pavadinimą (pavyzdžiui, x ), o tada - . . Tada ši išraiška konvertuoja išraišką į funkciją, kuri tikisi vieno parametro.
    Pavyzdžiui: λx.x+2 priima išraišką x+2 ir praneša, kad x simbolis šioje išraiška yra susietas kintamasis - jis gali būti pakeistas verte, kurią nurodote kaip parametrą.
    Atkreipkite dėmesį, kad tokiu būdu apibrėžta funkcija yra anoniminė - ji neturi pavadinimo, todėl negalite to dar nurodyti, bet tu gali ją nedelsiant pakviesti (prisiminti programą?), Nustatydami parametrą, kurį jis laukia, pavyzdžiui: (λx.x+2) 7 . Tada išraiška (šiuo atveju, pažodinė vertė) 7 pakeičiama x iš lambda subekspresijos x+2 , todėl jūs gaunate 7+2 , o tada sumažėja iki 9 pagal bendrąsias aritmetikos taisykles.

Taigi, nusprendėme vieną iš paslapčių:
lambda yra anoniminė funkcija iš aukščiau pateikto pavyzdžio, λx.x+2 .


Skirtingos programavimo kalbos gali turėti skirtingą funkcinę abstrakciją (lambda). Pavyzdžiui, „JavaScript“ atrodo taip:
 function(x) { return x+2; } 

ir jūs galite nedelsdami ją taikyti tam tikram parametrams:

 function(x) { return x+2; } (7) 

arba galite išsaugoti šią anoniminę funkciją (lambda) tam tikru kintamuoju:

 var f = function(x) { return x+2; } 

kuris iš tikrųjų suteikia jam pavadinimą f , leidžiantis jums tai paminėti ir kelis kartus paskambinti, pavyzdžiui:

 alert( f(7) + f(10) ); // should print 21 in the message box 

Bet jums nereikėjo jį paskambinti. Galite nedelsiant paskambinti jam:

 alert( function(x) { return x+2; } (7) ); // should print 9 in the message box 

LISP sistemoje lambdas sukuriami taip:

 (lambda (x) (+ x 2)) 

ir jūs galite skambinti tokiam lambda, jį nedelsiant pritaikydami prie parametro:

 ( (lambda (x) (+ x 2)) 7 ) 

<h / "> Gerai, dabar atėjo laikas išspręsti kitą paslaptį: kas yra uždarymas. Norėdami tai padaryti, leiskite kalbėti apie simbolius (kintamuosius) lambda išraiškose.

Kaip sakiau, kas lambda santrumpa yra reikalingas simbolis jo subekspresijoje, kad jis taptų keičiamu parametru. Toks simbolis vadinamas susietu. Bet kas, jei išraiškoje yra kitų simbolių? Pavyzdžiui: λx.x/y+2 . Šioje frazėje simbolis x yra susijęs su santrumpa lambda λx. prieš tai. Bet kitas y simbolis nėra susietas - jis yra nemokamas. Mes nežinome, kas tai yra arba iš kur ji kilo, todėl mes nežinome, ką tai reiškia ir kokią vertę ji reprezentuoja, todėl negalime įvertinti šios išraiškos tol, kol išsiaiškinsime, ką reiškia.

Iš tiesų tas pats atsitinka su dviem kitais simboliais: 2 ir + . Mes tik susipažinę su šiais dviem simboliais, kuriuos mes paprastai pamiršome, kad kompiuteris jų nežino, ir mes turime pasakyti, ką jie reiškia, kai, pavyzdžiui, juos apibrėžia. bibliotekoje ar pačioje kalboje.

Galite pagalvoti apie laisvus simbolius, apibrėžtus kitur, ne išraiškoje, „aplinkiniame kontekste“, kuris vadinamas jos aplinka . Aplinka gali būti didelė išraiška, kad ši išraiška yra dalis (kaip sakė Qui-Gon Jinn: „Visada yra didelė žuvis“);) arba kai kurioje bibliotekoje, arba pačioje kalboje (kaip primityvus).

Tai leidžia lambda išraiškas suskirstyti į dvi kategorijas:

  • Uždarytos išraiškos: kiekvienas simbolis, atsirandantis šiose išraiškose, yra susijęs su tam tikra lambda abstrakcija. Kitaip tariant, jie yra savarankiški; jiems nereikia įvertinti aplinkinių aplinkybių. Jie taip pat vadinami kombinatoriais.
  • OPEN išraiškos: kai kurie šių išraiškų simboliai nesusiję - tai yra, kai kurie jose esantys simboliai yra nemokami ir reikalauja tam tikros išorinės informacijos, todėl jie negali būti vertinami, kol nepateikiate šių simbolių apibrėžimų.

Galite uždaryti atvirą lambda išraišką, suteikdami aplinką, kuri apibrėžia visus šiuos laisvus simbolius, susiejant juos su kai kuriomis reikšmėmis (tai gali būti skaičiai, stygos, anoniminės funkcijos, žinomos kaip lambdas, nesvarbu ...).

Ir čia yra uždarymo dalis:
Lambda išraiškos uždarymas yra konkretus simbolių rinkinys, apibrėžtas išoriniame kontekste (aplinkoje), kuris suteikia reikšmes laisviems simboliams šioje išraiška, todėl jie tampa nemokesni. Pasirodo atvira lambda išraiška, kurioje vis dar yra „neapibrėžtų“ laisvų simbolių, į uždarą, kuris nebeturi jokių laisvų simbolių.

Pavyzdžiui, jei turite tokią lambda išraišką: λx.x/y+2 , x yra susijęs, o y nemokama, todėl išraiška yra open ir negali būti įvertinta, nebent jūs sakote, ką reiškia (ir tą patį) c + ir 2 , kurie taip pat yra nemokami). Bet tarkime, jūs taip pat turite tokią aplinką:

 { y: 3, +: [built-in addition], 2: [built-in number], q: 42, w: 5 } 

Ši aplinka apibrėžia visus „neapibrėžtus“ (nemokamai) simbolius iš mūsų lambda išraiškų ( y , + , 2 ) ir kelis papildomus simbolius ( q , w ). Ženklai, kuriuos reikia apibrėžti, yra aplinkos pogrupis:

 { y: 3, +: [built-in addition], 2: [built-in number] } 

ir tai tik mūsų lambda išraiškų uždarymas:>

Kitaip tariant, jis uždaro atvirą lambda išraišką. Būtent čia vardas buvo uždarytas, todėl daugelis atsakymų šioje temoje nėra visiškai teisingi: P


Tad kodėl jie neteisingi? Kodėl daugelis jų sako, kad uždarymas yra kai kurios atminties duomenų struktūros arba kai kurios jų vartojamų kalbų ypatybės, arba kodėl jie painioja uždarymus su lambdais?

Gerai, įmonių rinkai „Sun / Oracle“, „Microsoft“, „Google“ ir kt. kaltina, nes jie vadino šiuos konstruktus savo kalbomis („Java“, „C #, Go“ ir kt.). Jie dažnai vadinami „uždarymais“, kurie turėtų būti tiesiog lambdas. Arba jie vadina „uždarymą“ konkrečia technika, kurią jie panaudojo leksinei sričiai įgyvendinti, t.y. Tai, kad funkcija gali pasiekti kintamuosius, kurie apibrėžimo metu buvo apibrėžti išorinėje srityje. Jie dažnai sako, kad funkcija „supa“ šiuos kintamuosius, ty juos užfiksuoja tam tikroje duomenų struktūroje, kad jie nebūtų sunaikinti po išorinės funkcijos užbaigimo. Bet tai yra „post factum“ folkloro etimologija ir rinkodara, kuri tik apsunkina dalykus, nes kiekvienas kalbų teikėjas naudoja savo terminologiją.

Ir tai dar blogiau, nes tai, ką jie sako, visada turi mažą tiesą, kuri neleidžia lengvai jį atmesti kaip klaidingą: P Leiskite man paaiškinti:

Jei norite įdiegti kalbą, kuri naudoja lambda kaip pirmos klasės piliečius, turite leisti jiems naudoti simbolius, apibrėžtus jų aplinkoje (ty naudoti nemokamus kintamuosius jūsų lambdose). Ir šie simboliai turi būti ten, net jei grįžta į aplinkinę funkciją. Problema ta, kad šie simboliai yra susieti su tam tikra vietinės funkcijos saugykla (paprastai skambučių stekoje), kuri nebebus ten, kai funkcija bus grąžinta. Todėl, kad lambda galėtų dirbti taip, kaip tikėtasi, turite kažkaip „užfiksuoti“ visus šiuos laisvus kintamuosius iš išorinio konteksto ir išsaugoti juos vėliau, net jei išorinis kontekstas išnyksta. Tai reiškia, kad jums reikia rasti savo lambda uždarymą (visus šiuos išorinius kintamuosius, kuriuos jis naudoja) ir saugoti kitur (kurdami kopiją arba ruošdami jiems erdvę kitur, nei kamino vietoje). ). Tikrasis metodas, kurį naudojate šiam tikslui pasiekti, yra jūsų kalbos „įgyvendinimo detalė“. Svarbus dalykas čia yra uždarymas, kuris yra laisvo kintamųjų rinkinys iš jūsų lambda aplinkos, kurią reikia išsaugoti kažkur.

Prireikė per ilgai, kad žmonės pradėtų skambinti faktine duomenų struktūra, kurią jie naudoja savo kalbų įgyvendinime, kad užbaigtų uždarymą kaip „uždarymą“. Struktūra paprastai atrodo taip:

 Closure { [pointer to the lambda function machine code], [pointer to the lambda function environment] } 

ir šios duomenų struktūros perduodamos kaip parametrai kitoms funkcijoms, grąžinamos iš funkcijų ir saugomos kintamuosiuose, kad atstovautų „lambdas“ ir leistų jiems patekti į savo aplinką, taip pat mašinų kodą, kuris bus naudojamas šiame kontekste. Tačiau tai yra tik vienas būdas (vienas iš daugelio) įgyvendinti uždarymą, o ne pats uždarymas.

Kaip jau minėjau, lambda išraiškos uždarymas yra jo aplinkos apibrėžimų pogrupis, suteikiantis reikšmes laisviems kintamiesiems, esantiems šiame lambda išraiškoje, veiksmingai uždarant išraišką (atviros lambda išraiškos, kuri negali būti vertinama, bet uždaroje lambda išraiška, kad tada galima įvertinti, nes visi jame esantys simboliai dabar yra apibrėžti).

Visa kita yra tik programuotojų ir kalbų teikėjų „apkrovos kultas“ ir „stebuklinga magija“, kurie nežino tikrųjų šių sąvokų šaknų.

Tikiuosi, kad atsakys į jūsų klausimus. Bet jei turite klausimų dėl tolesnių veiksmų, nedvejodami paprašykite jų komentaruose, ir aš stengsiuosi tai geriau paaiškinti.

124
27 апр. atsakymas pateikiamas SasQ 27 balandžio. 2016-04-27 04:18 '16 at 4:18 2016-04-27 04:18

Ne visi uždarymai yra lambdas, o ne visi lambdai yra uždarymai. Abi yra funkcijos, bet nebūtinai taip, kaip anksčiau žinojome.

Lambda iš esmės yra funkcija, kuri apibrėžiama kaip integruotas, o ne standartinis metodas deklaruoti funkcijas. Lambdas dažnai gali būti perduodamas kaip objektai.

Uždarymas yra funkcija, kuri supa jo aplinką, nurodydama laukus, esančius už jos kūno. Įdėta valstybė lieka uždarymo skambučio forma.

Objektinės kalbos uždarymas paprastai teikiamas per objektus. Tačiau kai kurios OO kalbos (pvz., C #) įgyvendina specialiąsias funkcijas, kurios yra arčiau uždarymo apibrėžimų, teikiamų grynai funkcinėmis kalbomis (pvz., Lisp), kuriose nėra objektų, kad būtų galima įjungti būseną.

Įdomu tai, kad „Lambdas“ ir „Closures in C #“ įvedimas veda prie funkcinio programavimo prie pagrindinio naudojimo.

49
21 окт. Michael Brown atsakymas, pateiktas spalio 21 d 2008-10-21 06:29 '08, 6:29, 2008-10-21 06:29

Tai taip paprasta: lambda yra kalbos konstrukcija, t.y. tiesiog anoniminių funkcijų sintaksė; uždarymas yra būdas jį įgyvendinti - arba bet kokios pirmos klasės funkcijos šiuo klausimu yra vadinamos arba anoniminės.

Tiksliau sakant, uždarymas yra tai, kaip pirmos klasės funkcijos funkcija yra rodoma vykdymo metu, kaip „kodo“ ir aplinkos „uždarymo“ pora visiems šiame kode naudojamiems ne vietiniams kintamiesiems. Taigi šie kintamieji vis dar prieinami, net jei išorinės sritys, kuriose jos atsiranda, jau yra baigtos.

Deja, yra daug kalbų, kurios nepalaiko funkcijų, kaip pirmos klasės vertybės, arba palaiko jas tik sukrėsta forma. Todėl žmonės dažnai vartoja terminą „uždarymas“, kad būtų galima atskirti „tikrąjį dalyką“.

13
19 марта '14 в 11:31 2014-03-19 11:31 Atsakymą davė Andreas Rossberg kovo 19 d. 14 val. 11:31 2014-03-19 11:31

Programavimo kalbų požiūriu jie yra visiškai du skirtingi dalykai.

Iš esmės, visai Turingo kalbai, mums reikia tik labai ribotų elementų. abstrakcijos, programos ir santrumpos. Abstrakcija ir taikymas suteikia būdą, kaip sukurti išraišką lamdba, o redukcija nusausina lambda išraišką.

„Lambda“ suteikia galimybę suskaičiuoti skaičiavimo procesą. Pavyzdžiui, norint apskaičiuoti dviejų skaičių sumą, galima išskirti procesą, kuriame yra du parametrai x, y ir grąžina x + y. Diagramoje galite jį parašyti kaip

 (lambda (xy) (+ xy)) 

Galite pervardyti parametrus, bet jo atlikta užduotis nepasikeičia. Beveik visomis programavimo kalbomis galite suteikti lambda išraiškos pavadinimą, vadinamą funkcijomis. Tačiau nėra daug skirtumų, jie gali būti konceptualiai vertinami kaip tik sintaksinis cukrus.

Gerai, dabar įsivaizduokite, kaip tai galima įgyvendinti. Kai kai kurioms išraiškoms taikome lambda išraišką

 ((lambda (xy) (+ xy)) 2 3) 

Mes galime paprasčiausiai pakeisti parametrus išraiška, kurią reikia įvertinti. Šis modelis jau yra labai galingas. Tačiau šis modelis neleidžia mums keisti, pavyzdžiui, simbolių reikšmės. Negalime modeliuoti būsenos pakeitimo. Taigi mums reikia sudėtingesnio modelio. Trumpai tariant, kai norime apskaičiuoti lambda išraiškos vertę, mes įterpiame porą simbolių ir atitinkamą vertę laikmenoje (arba lentelėje). Tada kiti (+ xy) vertinami ieškant atitinkamų simbolių lentelėje. Dabar, jei mes teikiame tam tikrus primityvus tiesioginiam naudojimui aplinkoje, galime imituoti būsenos pasikeitimus!

Šiame fone patikrinkite šią funkciją:

 (lambda (xy) (+ xyz)) 

Mes žinome, kad vertindami lambda išraišką, xy bus įpareigota naujame stende. Bet kaip ir kur mes galime ieškoti? Tiesą sakant, z yra laisvas kintamasis. Turi būti išorinė aplinka, kurioje yra z. Priešingu atveju, išraiškos reikšmę negali nustatyti tik x ir y susiejimas. Norėdami tai padaryti, galite rašyti ką nors diagramoje:

 ((lambda (z) (lambda (xy) (+ xyz))) 1) 

Taigi, z bus prijungtas prie 1 išorinėje lentelėje. Mes vis dar gauname funkciją, kuri užima du parametrus, tačiau tikra šio reiškinio reikšmė priklauso ir nuo išorinės aplinkos. Kitaip tariant, išorinė aplinka yra uždaryta laisviems kintamiesiems. Su rinkiniu!, Mes galime atlikti valstybinę funkciją, t.y. Tai nėra funkcija matematikos prasme. Tai, ką jis grąžina, priklauso ne tik nuo įvesties, bet ir nuo z.

Tai yra kažkas, ką jūs labai gerai žinote, objektų metodas beveik visada priklauso nuo objektų būklės. Štai kodėl kai kurie žmonės sako, kad „uždarymas yra blogi žmogaus objektai“. Bet mes taip pat galėtume peržiūrėti objektus kaip uždarytus žmones, nes mums tikrai patinka pirmos klasės funkcijos.

Aš naudoju schemą, skirtą iliustruoti su šia schema susijusias idėjas, yra viena iš pirmųjų kalbų, turinčių tikrą uždarymą. Все материалы здесь намного лучше представлены в главе 3 SICP.