Qual è la differenza tra una 'chiusura' e una 'lambda'?


811

Qualcuno potrebbe spiegare? Capisco i concetti di base dietro di loro ma spesso li vedo usati in modo intercambiabile e mi confondo.

E ora che siamo qui, in cosa differiscono da una normale funzione?


85
Le lambda sono un costrutto linguistico (funzioni anonime), le chiusure sono una tecnica di implementazione per implementare funzioni di prima classe (anonime o meno). Sfortunatamente, questo è spesso confuso da molte persone.
Andreas Rossberg,



Per le chiusure PHP, consultare php.net/manual/en/class.closure.php . Non è quello che un programmatore JavaScript si aspetterebbe.
Paolo

La risposta di SasQ è eccellente. IMHO questa domanda sarebbe più utile per gli utenti SO se guidasse gli spettatori a quella risposta.
AmigoNico,

Risposte:


704

Una lambda è solo una funzione anonima, una funzione definita senza nome. In alcune lingue, come Scheme, sono equivalenti alle funzioni con nome. In effetti, la definizione della funzione viene riscritta come associazione interna di una lambda a una variabile. In altre lingue, come Python, ci sono alcune (piuttosto inutili) distinzioni tra loro, ma si comportano allo stesso modo in caso contrario.

Una chiusura è qualsiasi funzione che chiude sopra l' ambiente in cui è stato definito. Ciò significa che può accedere a variabili non presenti nell'elenco dei parametri. Esempi:

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

Ciò causerà un errore, perché funcnon si chiude sull'ambiente in anotherfunc- hnon è definito. funcsi chiude solo sull'ambiente globale. Questo funzionerà:

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

Perché qui, funcè definito in anotherfunc, e in Python 2.3 e versioni successive (o in alcuni numeri come questo) quando hanno quasi corretto le chiusure (la mutazione continua a non funzionare), ciò significa che si chiude anotherfunc sull'ambiente e può accedere alle variabili all'interno di esso. In Python 3.1+, anche la mutazione funziona quando si usa la nonlocalparola chiave .

Un altro punto importante: funccontinuerà a chiudere l' anotherfuncambiente anche quando non viene più valutato anotherfunc. Questo codice funzionerà anche:

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

print anotherfunc(10)()

Questo stamperà 10.

Questo, come notate, non ha nulla a che fare con lambda s - sono due concetti diversi (sebbene correlati).


1
Claudiu, per mia incerta conoscenza, Python non ha mai corretto le chiusure. Hanno risolto il problema della mutabilità mentre non stavo guardando? Abbastanza possibile ...
simon,

simon - hai ragione, non sono ancora completamente corretti. Penso che Python 3.0 aggiungerà un trucco per aggirare questo. (qualcosa come un identificatore "non locale"). cambierò il mio ansewr per riflettere quello.
Claudiu,

2
@AlexanderOrlov: sono sia lambda che chiusure. Java aveva chiuso prima tramite classi interne anonime. Ora questa funzionalità è stata resa sintatticamente più semplice tramite le espressioni lambda. Quindi probabilmente l'aspetto più rilevante della nuova funzionalità è che ora ci sono lambda. Non è errato chiamarli lambda, sono davvero lambda. Perché gli autori di Java 8 potrebbero scegliere di non evidenziare il fatto che si tratta di chiusure non è qualcosa che conosco.
Claudiu,

2
@AlexanderOrlov perché Java 8 lambda non sono vere chiusure, sono simulazioni di chiusure. Sono più simili alle chiusure di Python 2.3 (nessuna mutabilità, quindi il requisito per le variabili indicate come "effettivamente definitive") e si compilano internamente in funzioni di non chiusura che prendono tutte le variabili a cui si fa riferimento nell'ambito compreso come parametri nascosti.
Logan Pickup,

7
@Claudiu Penso che il riferimento ad una particolare implementazione del linguaggio (Python) possa complicare eccessivamente la risposta. La domanda è completamente indipendente dalla lingua (così come non ha tag specifici della lingua).
Matteo

319

C'è molta confusione tra lambda e chiusure, anche nelle risposte a questa domanda StackOverflow qui. Invece di chiedere ai programmatori casuali che hanno appreso delle chiusure dalla pratica con determinati linguaggi di programmazione o altri programmatori indecisi, fai un viaggio verso la fonte (dove tutto è iniziato). E poiché lambda e chiusure provengono da Lambda Calculus inventato dalla Chiesa Alonzo negli anni '30 prima ancora che esistessero i primi computer elettronici, questa è la fonte di cui sto parlando.

Lambda Calculus è il linguaggio di programmazione più semplice al mondo. Le uniche cose che puoi fare in esso: ►

  • APPLICAZIONE: applicazione di un'espressione a un'altra, indicata f x.
    (Consideralo come una chiamata di funzione , dov'è fla funzione ed xè il suo unico parametro)
  • ASTRAZIONE: lega un simbolo che si verifica in un'espressione per indicare che questo simbolo è solo uno "slot", una casella vuota in attesa di essere riempita di valore, una "variabile" per così dire. Viene fatto anteponendo una lettera greca λ(lambda), quindi il nome simbolico (ad esempio x), quindi un punto .prima dell'espressione. Questo quindi converte l'espressione in una funzione in attesa di un parametro .
    Ad esempio: λx.x+2prende l'espressione x+2e dice che il simbolo xin questa espressione è una variabile associata : può essere sostituito con un valore fornito come parametro.
    Si noti che la funzione definita in questo modo è anonima- non ha un nome, quindi non è possibile fare riferimento ad esso ancora, ma è possibile chiamare immediatamente esso (ricordate applicazione?) Fornendogli il parametro è in attesa di, come questo: (λx.x+2) 7. Quindi l'espressione (in questo caso un valore letterale) 7viene sostituita come xnella sottoespressione x+2della lambda applicata, in modo da ottenere 7+2, che quindi si riduce a 9regole aritmetiche comuni.

Così abbiamo risolto uno dei misteri:
lambda è la funzione anonima dall'esempio precedente, λx.x+2.


In diversi linguaggi di programmazione, la sintassi per l'astrazione funzionale (lambda) può differire. Ad esempio, in JavaScript è simile al seguente:

function(x) { return x+2; }

e puoi applicarlo immediatamente ad alcuni parametri come questo:

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

oppure puoi memorizzare questa funzione anonima (lambda) in qualche variabile:

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

che gli dà effettivamente un nome f, permettendoti di fare riferimento ad esso e chiamarlo più volte in seguito, ad esempio:

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

Ma non dovevi nominarlo. Puoi chiamarlo immediatamente:

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

In LISP, i lambda sono fatti così:

(lambda (x) (+ x 2))

e puoi chiamare un tale lambda applicandolo immediatamente a un parametro:

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


OK, ora è il momento di risolvere l'altro mistero: cos'è una chiusura . Per fare ciò, parliamo di simboli ( variabili ) nelle espressioni lambda.

Come ho detto, ciò che fa l'astrazione lambda è legare un simbolo nella sua sottoespressione, in modo che diventi un parametro sostituibile . Un tale simbolo si chiama legato . E se ci fossero altri simboli nell'espressione? Ad esempio: λx.x/y+2. In questa espressione, il simbolo xè legato dall'astrazione lambda λx.che lo precede. Ma l'altro simbolo, ynon è vincolato, è gratuito . Non sappiamo cosa sia e da dove venga, quindi non sappiamo cosa significhi e quale valore rappresenti, e quindi non possiamo valutare quell'espressione fino a quando non capiamo cosa ysignifichi.

In effetti, lo stesso vale con gli altri due simboli 2e +. È solo che conosciamo così bene questi due simboli che di solito dimentichiamo che il computer non li conosce e dobbiamo dirgli cosa significano definendoli da qualche parte, ad esempio in una biblioteca o nella lingua stessa.

Puoi pensare ai simboli liberi come definiti altrove, al di fuori dell'espressione, nel suo "contesto circostante", che è chiamato il suo ambiente . L'ambiente potrebbe essere un'espressione più grande di cui questa espressione fa parte (come diceva Qui-Gon Jinn: "C'è sempre un pesce più grande";)), o in qualche biblioteca, o nella lingua stessa (come una primitiva ).

Questo ci consente di dividere le espressioni lambda in due categorie:

  • Espressioni CHIUSE: ogni simbolo che si manifesta in queste espressioni è legato da un'astrazione lambda. In altre parole, sono autosufficienti ; non richiedono alcun contesto circostante per essere valutati. Sono anche chiamati combinatori .
  • Espressioni APERTE: alcuni simboli in queste espressioni non sono associati , ovvero alcuni dei simboli presenti in esse sono gratuiti e richiedono alcune informazioni esterne, pertanto non possono essere valutati fino a quando non vengono fornite le definizioni di questi simboli.

Puoi CHIUDERE un'espressione lambda aperta fornendo l' ambiente , che definisce tutti questi simboli liberi legandoli ad alcuni valori (che possono essere numeri, stringhe, funzioni anonime come lambda, qualunque cosa ...).

E qui arriva la parte di chiusura :
la chiusura di un'espressione lambda è questo particolare insieme di simboli definiti nel contesto esterno (ambiente) che danno valori ai simboli liberi in questa espressione, rendendoli più non liberi. Si scopre una aperta espressione lambda, che contiene ancora alcuni "indefinito" simboli liberi, in un chiuso uno, che non ha più alcun simbolo liberi.

Ad esempio, se hai la seguente espressione lambda:, λx.x/y+2il simbolo xè associato, mentre il simbolo yè libero, quindi l'espressione è opene non può essere valutata a meno che tu non dica cosa ysignifica (e lo stesso con +e 2, che sono anche liberi). Supponiamo che tu abbia anche un ambiente come questo:

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

Questo ambiente fornisce le definizioni per tutti i simboli "indefinito" (gratuito) dalla nostra espressione lambda ( y, +, 2), e diversi simboli aggiuntivi ( q, w). I simboli che dobbiamo definire sono questo sottoinsieme dell'ambiente:

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

e questa è precisamente la chiusura della nostra espressione lambda:>

In altre parole, chiude un'espressione lambda aperta. Ecco da dove viene la chiusura del nome in primo luogo, ed è per questo che le risposte di così tante persone in questo thread non sono del tutto corrette: P


Allora perché si sbagliano? Perché così tanti di loro dicono che le chiusure sono alcune strutture di dati in memoria, o alcune caratteristiche dei linguaggi che usano, o perché confondono le chiusure con lambdas? : P

Bene, i responsabili aziendali di Sun / Oracle, Microsoft, Google ecc. Sono da biasimare, perché è quello che hanno chiamato questi costrutti nelle loro lingue (Java, C #, Go ecc.). Spesso chiamano "chiusure" quelli che dovrebbero essere solo lambda. Oppure chiamano "chiusure" una particolare tecnica utilizzata per implementare l'ambito lessicale, ovvero il fatto che una funzione può accedere alle variabili che sono state definite nel suo ambito esterno al momento della sua definizione. Spesso affermano che la funzione "racchiude" queste variabili, ovvero le acquisisce in una struttura di dati per salvarle dalla distruzione al termine dell'esecuzione della funzione esterna. Ma si tratta solo di "etimologia folcloristica" post - marketing inventata e di marketing, il che rende le cose più confuse,

Ed è anche peggio a causa del fatto che c'è sempre un po 'di verità in quello che dicono, che non ti consente di liquidarlo facilmente come falso: P Lasciami spiegare:

Se vuoi implementare un linguaggio che usa lambda come cittadini di prima classe, devi consentire loro di usare simboli definiti nel loro contesto circostante (cioè, di usare variabili libere nei tuoi lambda). E questi simboli devono essere presenti anche quando ritorna la funzione circostante. Il problema è che questi simboli sono associati a una memoria locale della funzione (di solito nello stack di chiamate), che non sarà più presente quando la funzione ritorna. Pertanto, affinché una lambda funzioni nel modo previsto, è necessario in qualche modo "catturare" tutte queste variabili libere dal suo contesto esterno e salvarle per dopo, anche quando il contesto esterno sparirà. Cioè, devi trovare la chiusuradel tuo lambda (tutte queste variabili esterne che utilizza) e memorizzalo altrove (o facendo una copia, o preparando spazio per esse in anticipo, da qualche altra parte rispetto allo stack). Il metodo effettivo che usi per raggiungere questo obiettivo è un "dettaglio di implementazione" della tua lingua. Quello che è importante qui è la chiusura , che è l'insieme di variabili libere dal contesto della tua lambda che hanno bisogno di essere salvato da qualche parte.

Non ci volle troppo tempo perché le persone iniziassero a chiamare la struttura di dati effettiva che usano nelle implementazioni del loro linguaggio per implementare la chiusura come la "chiusura" stessa. La struttura di solito assomiglia a questo:

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

e queste strutture di dati vengono trasferite come parametri ad altre funzioni, restituite da funzioni e archiviate in variabili, per rappresentare lambdas e consentire loro di accedere al loro ambiente di chiusura e al codice macchina da eseguire in quel contesto. Ma è solo un modo (uno dei tanti) per implementare la chiusura, non la chiusura stessa.

Come ho spiegato sopra, la chiusura di un'espressione lambda è il sottoinsieme di definizioni nel suo ambiente che danno valori alle variabili libere contenute in quell'espressione lambda, chiudendo effettivamente l'espressione (trasformando un'espressione lambda aperta , che non può ancora essere valutata, in un chiuso espressione lambda, che possono poi essere valutata, poiché tutti i simboli in esso contenuti vengono ora definiti).

Qualsiasi altra cosa è solo un "culto del carico" e una "magia voo-doo" di programmatori e venditori di lingue ignari delle vere radici di queste nozioni.

Spero che risponda alle tue domande. Ma se hai domande di follow-up, sentiti libero di farle nei commenti e proverò a spiegarlo meglio.


50
La migliore risposta spiegando cose genericamente piuttosto che specifiche della lingua
Shishir Arora,

39
adoro questo tipo di approccio quando spiega le cose. A partire dall'inizio, spiegando come funzionano le cose e poi come sono state create le idee sbagliate correnti. Questa risposta deve andare in cima.
Sharky,

1
@SasQ> la chiusura di un'espressione lambda è il sottoinsieme di definizioni nel suo ambiente che danno valori alle variabili libere contenute in quell'espressione lambda <Quindi nella tua struttura di dati di esempio, ciò significa che è più appropriato dire il "puntatore al L'ambiente della funzione lambda "è la chiusura?
Jeff M,

3
Sebbene il calcolo Lambda mi sembri un linguaggio macchina per me, devo ammettere che si tratta di un linguaggio "trovato" in contrasto con un linguaggio "fatto". E quindi molto meno soggetto a convenzioni arbitrarie e molto più adatto a catturare la struttura sottostante della realtà. Possiamo trovare dettagli in Linq, JavaScript, F # più accessibili / accessibili, ma il calcolo Lambda arriva al nocciolo della questione senza distrazione.
StevePoling

1
Apprezzo che tu abbia ribadito il tuo punto più volte, con parole leggermente diverse ogni volta. Aiuta a rafforzare il concetto. Vorrei che più persone lo facessero.
johnklawlor,

175

Quando la maggior parte delle persone pensa alle funzioni , pensa alle funzioni denominate :

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

Questi sono chiamati per nome, ovviamente:

foo(); //returns the string above

Con le espressioni lambda , puoi avere funzioni anonime :

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

Con l'esempio sopra, puoi chiamare la lambda attraverso la variabile a cui è stata assegnata:

foo();

Più utile dell'assegnazione di funzioni anonime alle variabili, tuttavia, le stanno passando ao da funzioni di ordine superiore, ovvero funzioni che accettano / restituiscono altre funzioni. In molti di questi casi, la denominazione di una funzione non è necessaria:

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

Una chiusura può essere una funzione denominata o anonima, ma è nota come tale quando "si chiude" sulle variabili nell'ambito in cui è definita la funzione, ovvero la chiusura farà ancora riferimento all'ambiente con eventuali variabili esterne utilizzate nella chiusura stessa. Ecco una chiusura denominata:

@x = 0;

function incrementX() { x = x + 1;}

incrementX(); // x now equals 1

Non sembra molto, ma cosa succederebbe se tutto fosse in un'altra funzione e passassi incrementXa una funzione esterna?

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 (y.x == 0 + 1)
y(); //returns 2 (y.x == 1 + 1)

Ecco come ottenere oggetti con stato nella programmazione funzionale. Poiché la denominazione "incrementX" non è necessaria, è possibile utilizzare un lambda in questo caso:

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }

14
che lingua stai qui?
Claudiu,

7
Fondamentalmente è pseudocodice. C'è un po 'di lisp e JavaScript in esso, oltre a una lingua che sto progettando chiamata "@" ("at"), che prende il nome dall'operatore di dichiarazione delle variabili.
Mark Cidade,

3
@MarkCidade, quindi dov'è questa lingua @? Esiste una documentazione e un download?
Pacerier,

5
Perché non prendere Javascript e aggiungere un vincolo di dichiarazione delle variabili con il segno @ iniziale? Ciò farebbe risparmiare un po 'di tempo :)
Nemoden,

5
@Pacerier: ho iniziato a implementare la lingua: github.com/marxidad/At2015
Mark Cidade

54

Non tutte le chiusure sono lambda e non tutte le chiusure sono chiusure. Entrambe sono funzioni, ma non necessariamente nel modo in cui siamo abituati a conoscere.

Una lambda è essenzialmente una funzione definita in linea anziché il metodo standard di dichiarazione delle funzioni. Le lambda possono spesso essere passate in giro come oggetti.

Una chiusura è una funzione che racchiude il suo stato circostante facendo riferimento a campi esterni al suo corpo. Lo stato chiuso rimane attraverso le invocazioni della chiusura.

In un linguaggio orientato agli oggetti, le chiusure vengono normalmente fornite attraverso gli oggetti. Tuttavia, alcuni linguaggi OO (ad es. C #) implementano funzionalità speciali che sono più vicine alla definizione di chiusure fornite da linguaggi puramente funzionali (come lisp) che non hanno oggetti per racchiudere lo stato.

La cosa interessante è che l'introduzione di Lambdas e Closures in C # avvicina la programmazione funzionale all'utilizzo tradizionale.


Quindi, potremmo dire che le chiusure sono un sottoinsieme di lambda e le lambde sono un sottoinsieme di funzioni?
sciatore

Le chiusure sono un sottoinsieme di lambda ... ma le lambda sono più speciali delle normali funzioni. Come ho detto, i lambda sono definiti in linea. In sostanza, non è possibile fare riferimento a loro se non vengono passati a un'altra funzione o restituiti come valore di ritorno.
Michael Brown,

19
Le lambda e le chiusure sono ciascuna un sottoinsieme di tutte le funzioni, ma esiste solo un'intersezione tra lambde e chiusure, in cui la parte non chiusa delle chiusure viene denominata funzioni che sono chiusure e le lamelle non intersecanti sono funzioni autonome con variabili associate.
Mark Cidade,

Secondo me, i lambda sono concetti più fondamentali delle funzioni. Dipende molto dal linguaggio di programmazione.
Wei Qiu,

15

È semplice come questo: lambda è un costrutto di linguaggio, cioè semplicemente sintassi per funzioni anonime; una chiusura è una tecnica per implementarla - o qualsiasi funzione di prima classe, per quella materia, nominata o anonima.

Più precisamente, una chiusura è il modo in cui una funzione di prima classe è rappresentata in fase di esecuzione, come una coppia del suo "codice" e un ambiente che "chiude" su tutte le variabili non locali utilizzate in quel codice. In questo modo, tali variabili sono ancora accessibili anche quando sono già usciti gli ambiti esterni da cui hanno origine.

Sfortunatamente, ci sono molte lingue là fuori che non supportano le funzioni come valori di prima classe, o le supportano solo in forma paralizzata. Quindi le persone usano spesso il termine "chiusura" per distinguere "la cosa reale".


12

Dal punto di vista dei linguaggi di programmazione, sono completamente due cose diverse.

Fondamentalmente per un linguaggio completo di Turing abbiamo solo bisogno di elementi molto limitati, ad esempio astrazione, applicazione e riduzione. L'astrazione e l'applicazione forniscono il modo in cui è possibile creare l'espressione lamdba e la riduzione riduce il significato dell'espressione lambda.

Lambda fornisce un modo per estrarre il processo di calcolo. per esempio, per calcolare la somma di due numeri, un processo che accetta due parametri x, y e restituisce x + y può essere estratto. Nello schema, puoi scriverlo come

(lambda (x y) (+ x y))

È possibile rinominare i parametri, ma l'attività che completa non cambia. In quasi tutti i linguaggi di programmazione, puoi dare un nome all'espressione lambda, che sono denominate funzioni. Ma non c'è molta differenza, possono essere concettualmente considerati solo zucchero di sintassi.

OK, ora immagina come può essere implementato. Ogni volta che applichiamo l'espressione lambda ad alcune espressioni, ad es

((lambda (x y) (+ x y)) 2 3)

Possiamo semplicemente sostituire i parametri con l'espressione da valutare. Questo modello è già molto potente. Ma questo modello non ci consente di modificare i valori dei simboli, ad esempio non possiamo imitare il cambiamento di stato. Quindi abbiamo bisogno di un modello più complesso. Per farla breve, ogni volta che vogliamo calcolare il significato dell'espressione lambda, inseriamo la coppia di simboli e il valore corrispondente in un ambiente (o tabella). Quindi il resto (+ xy) viene valutato cercando i simboli corrispondenti nella tabella. Ora, se forniamo alcune primitive per operare direttamente sull'ambiente, possiamo modellare i cambiamenti di stato!

Con questo sfondo, controlla questa funzione:

(lambda (x y) (+ x y z))

Sappiamo che quando valutiamo l'espressione lambda, xy verrà associato in una nuova tabella. Ma come e dove possiamo cercare z? In realtà z si chiama una variabile libera. Deve esserci un ambiente esterno contenente z. Altrimenti il ​​significato dell'espressione non può essere determinato legando solo xey. Per chiarire questo, puoi scrivere qualcosa come segue nello schema:

((lambda (z) (lambda (x y) (+ x y z))) 1)

Quindi z sarebbe associato a 1 in una tabella esterna. Otteniamo ancora una funzione che accetta due parametri, ma il suo significato reale dipende anche dall'ambiente esterno. In altre parole, l'ambiente esterno si chiude sulle variabili libere. Con l'aiuto di set !, possiamo rendere la funzione stateful, cioè non è una funzione nel senso della matematica. Ciò che restituisce non dipende solo dall'input, ma anche da z.

Questo è qualcosa che già conosci molto bene, un metodo di oggetti si basa quasi sempre sullo stato degli oggetti. Ecco perché alcune persone affermano che "le chiusure sono oggetti dei poveri". Ma potremmo anche considerare gli oggetti come chiusure dei poveri poiché ci piacciono molto le funzioni di prima classe.

Uso schema per illustrare le idee a causa di quello schema è uno dei primi linguaggi che ha chiusure reali. Tutti i materiali qui sono presentati molto meglio nel capitolo 3 della SICP.

Per riassumere, lambda e chiusura sono concetti davvero diversi. Una lambda è una funzione. Una chiusura è una coppia di lambda e l'ambiente corrispondente che chiude la lambda.


Quindi si potrebbero sostituire tutte le chiusure con lambda annidate fino a quando non sono più presenti variabili libere? In questo caso, direi che le chiusure potrebbero essere viste come un tipo speciale di lambda.
Trilarion,

8

Il concetto è lo stesso descritto sopra, ma se si proviene da background PHP, questo spiega ulteriormente l'utilizzo del codice PHP.

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, function ($v) { return $v > 2; });

funzione ($ v) {return $ v> 2; } è la definizione della funzione lambda. Possiamo persino memorizzarlo in una variabile, quindi può essere riutilizzabile:

$max = function ($v) { return $v > 2; };

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max);

Ora, cosa succede se si desidera modificare il numero massimo consentito nell'array filtrato? Dovresti scrivere un'altra funzione lambda o creare una chiusura (PHP 5.3):

$max_comp = function ($max) {
  return function ($v) use ($max) { return $v > $max; };
};

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max_comp(2));

Una chiusura è una funzione che viene valutata nel proprio ambiente, che ha una o più variabili associate a cui è possibile accedere quando viene chiamata la funzione. Provengono dal mondo della programmazione funzionale, dove ci sono una serie di concetti in gioco. Le chiusure sono come le funzioni lambda, ma più intelligenti nel senso che hanno la capacità di interagire con le variabili dall'ambiente esterno in cui è definita la chiusura.

Ecco un esempio più semplice di chiusura di PHP:

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

$closure();

Ben spiegato in questo articolo.


7

Questa domanda è vecchia e ha ottenuto molte risposte.
Ora con Java 8 e Official Lambda che sono progetti di chiusura non ufficiali, fa rivivere la domanda.

La risposta nel contesto Java (tramite Lambdas e chiusure: qual è la differenza? ):

"Una chiusura è un'espressione lambda accoppiata con un ambiente che lega ciascuna delle sue variabili libere a un valore. In Java, le espressioni lambda verranno implementate mediante chiusure, quindi i due termini sono stati usati in modo intercambiabile nella comunità."


In che modo i Lamda sono implementati dalla chiusura in Java? Significa che l'espressione di Lamda viene convertita in una classe anonima di vecchio stile?
hackjutsu,

5

In parole povere, la chiusura è un trucco sull'ambito, lambda è una funzione anonima. Possiamo realizzare la chiusura con lambda in modo più elegante e lambda viene spesso utilizzato come parametro passato a una funzione superiore


4

Un'espressione Lambda è solo una funzione anonima. in java semplice, ad esempio, puoi scriverlo in questo modo:

Function<Person, Job> mapPersonToJob = new Function<Person, Job>() {
    public Job apply(Person person) {
        Job job = new Job(person.getPersonId(), person.getJobDescription());
        return job;
    }
};

dove la classe Function è appena costruita nel codice java. Ora puoi chiamare mapPersonToJob.apply(person)da qualche parte per usarlo. questo è solo un esempio. Quello è un lambda prima che ci fosse sintassi per esso. Lambdas una scorciatoia per questo.

Chiusura:

una Lambda diventa una chiusura quando può accedere alle variabili al di fuori di questo ambito. immagino che tu possa dire la sua magia, magicamente può avvolgere l'ambiente in cui è stato creato e usare le variabili al di fuori del suo ambito (ambito esterno. Quindi, per essere chiari, una chiusura significa che un lambda può accedere al suo SCOPO ESTERNO.

in Kotlin, una lambda può sempre accedere alla sua chiusura (le variabili che si trovano nel suo ambito esterno)


0

Dipende dal fatto che una funzione usi o meno una variabile esterna per eseguire l'operazione.

Variabili esterne : variabili definite al di fuori dell'ambito di una funzione.

  • Le espressioni lambda sono senza stato perché dipendono da parametri, variabili interne o costanti per eseguire operazioni.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • Le chiusure mantengono lo stato perché utilizzano variabili esterne (ovvero variabili definite al di fuori dell'ambito del corpo della funzione) insieme a parametri e costanti per eseguire operazioni.

    int n = 2
    
    Function<Integer,Integer> closure = t -> {
        return t * n 
    }
    

Quando Java crea la chiusura, mantiene la variabile n con la funzione in modo che possa essere referenziata quando passata ad altre funzioni o utilizzata ovunque.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.