Expresii Lambda în C ++

Lambda Expressions C



De ce Lambda Expression?

Luați în considerare următoarea afirmație:

intmyInt= 52;

Aici, myInt este un identificator, o valoare. 52 este un literal, o valoare. Astăzi, este posibil să codați o funcție special și să o puneți în poziția 52. O astfel de funcție se numește expresie lambda. Luați în considerare și următorul program scurt:







#include

folosind spațiu de numeore;

intfn(intprin)

{

intRăspuns=prin+ 3;

întoarcereRăspuns;

}


intprincipal()

{

fn(5);



întoarcere 0;

}

Astăzi, este posibil să codați o funcție special și să o puneți în poziția argumentului 5, al funcției apel, fn (5). O astfel de funcție se numește expresie lambda. Expresia (funcția) lambda din acea poziție este o valoare.



Orice literal, cu excepția șirului literal, este o valoare. Expresia lambda este un design de funcție special care s-ar potrivi ca un literal în cod. Este o funcție anonimă (fără nume). Acest articol explică noua expresie primară C ++, numită expresie lambda. Cunoașterea de bază în C ++ este o cerință pentru a înțelege acest articol.



Conținutul articolului

Ilustrația expresiei Lambda

În următorul program, o funcție, care este o expresie lambda, este atribuită unei variabile:





#include

folosind spațiu de numeore;

autofn= [](intStop)

{

intRăspuns=Stop+ 3;

întoarcereRăspuns;

};


intprincipal()

{

autovariab=fn(2);

cost <<variab<< ' n';


întoarcere 0;

}

Ieșirea este:

5

În afara funcției main (), există variabila, fn. Tipul său este auto. Auto în această situație înseamnă că tipul real, cum ar fi int sau float, este determinat de operandul corect al operatorului de atribuire (=). În dreapta operatorului de atribuire este o expresie lambda. O expresie lambda este o funcție fără tipul de returnare precedent. Rețineți utilizarea și poziția parantezelor pătrate, []. Funcția returnează 5, un int, care va determina tipul pentru fn.



În funcția main (), există declarația:

autovariab=fn(2);

Aceasta înseamnă că, fn afara main (), ajunge ca identificator pentru o funcție. Parametrii săi implicați sunt cei ai expresiei lambda. Tipul pentru variabila este auto.

Rețineți că expresia lambda se termină cu punct și virgulă, la fel ca definiția clasei sau structurilor, se termină cu punct și virgulă.

În următorul program, o funcție, care este o expresie lambda care returnează valoarea 5, este un argument pentru o altă funcție:

#include

folosind spațiu de numeore;

nulotherfn(intnumarul 1,int (*ptr)(int))

{

intnr2= (*ptr)(2);

cost <<numarul 1<< '' <<nr2<< ' n';

}


intprincipal()

{

otherfn(4,[](intStop)

{

intRăspuns=Stop+ 3;

întoarcereRăspuns;

});


întoarcere 0;
}

Ieșirea este:

Patru cinci

Există două funcții aici, expresia lambda și funcția otherfn (). Expresia lambda este al doilea argument al otherfn (), numit în main (). Rețineți că funcția lambda (expresia) nu se termină cu un punct și virgulă în acest apel deoarece, aici, este un argument (nu o funcție autonomă).

Parametrul funcției lambda din definiția funcției otherfn () este un indicator către o funcție. Pointerul are numele, ptr. Numele, ptr, este utilizat în definiția otherfn () pentru a apela funcția lambda.

Declaratia,

intnr2= (*ptr)(2);

În definiția otherfn (), apelează funcția lambda cu un argument de 2. Valoarea returnată a apelului, '(* ptr) (2)' din funcția lambda, este atribuită no2.

Programul de mai sus arată, de asemenea, modul în care funcția lambda poate fi utilizată în schema funcției de apelare C ++.

Părți ale expresiei Lambda

Părțile unei funcții tipice lambda sunt după cum urmează:

[] () {}
  • [] este clauza de captare. Poate avea articole.
  • () este pentru lista de parametri.
  • {} este pentru corpul funcției. Dacă funcția stă singură, atunci ar trebui să se încheie cu un punct și virgulă.

Capturi

Definiția funcției lambda poate fi atribuită unei variabile sau utilizată ca argument pentru un apel de funcție diferit. Definiția pentru un astfel de apel funcțional ar trebui să aibă ca parametru, un pointer către o funcție, corespunzător definiției funcției lambda.

Definiția funcției lambda este diferită de definiția funcției normale. Poate fi atribuit unei variabile din domeniul global; această funcție atribuită variabilei poate fi, de asemenea, codificată într-o altă funcție. Atunci când este atribuită unei variabile de domeniu global, corpul său poate vedea alte variabile în domeniul de aplicare global. Atunci când este atribuită unei variabile din cadrul unei definiții normale a funcției, corpul său poate vedea alte variabile în sfera funcției numai cu ajutorul clauzei de captură, [].

Clauza de captură [], cunoscută și sub numele de introdus lambda, permite trimiterea variabilelor din domeniul (funcției) înconjurătoare în corpul funcției expresiei lambda. Se spune că corpul funcției expresiei lambda captează variabila atunci când primește obiectul. Fără clauza de captură [], o variabilă nu poate fi trimisă din sfera înconjurătoare în corpul funcției expresiei lambda. Următorul program ilustrează acest lucru, cu scopul funcției main (), ca domeniul înconjurător:

#include

folosind spațiu de numeore;

intprincipal()

{

intid= 5;


autofn= [id]()

{

cost <<id<< ' n';

};

fn();


întoarcere 0;

}

Ieșirea este 5 . Fără numele, id, în interiorul [], expresia lambda nu ar fi văzut variabila id a sferei funcției main ().

Captarea prin referință

Exemplul de utilizare de mai sus a clauzei de captare este captarea după valoare (vezi detaliile de mai jos). În capturarea prin referință, locația (stocarea) variabilei, de exemplu, id-ul de mai sus, al scopului înconjurător, este pusă la dispoziție în interiorul corpului funcției lambda. Deci, schimbarea valorii variabilei din corpul funcției lambda va schimba valoarea aceleiași variabile în domeniul înconjurător. Fiecare variabilă repetată în clauza de captură este precedată de ampersand (&) pentru a realiza acest lucru. Următorul program ilustrează acest lucru:

#include

folosind spațiu de numeore;

intprincipal()

{

intid= 5; plutift= 2.3; charcap= 'LA';

autofn= [&id,&ft,&cap]()

{

id= 6;ft= 3.4;cap= „B”;

};

fn();

cost <<id<< ',' <<ft<< ',' <<cap<< ' n';

întoarcere 0;

}

Ieșirea este:

6, 3.4, B

Confirmând că numele variabilelor din corpul funcției expresiei lambda sunt pentru aceleași variabile în afara expresiei lambda.

Captarea după valoare

În capturarea în funcție de valoare, o copie a locației variabilei, a domeniului înconjurător, este pusă la dispoziție în corpul funcției lambda. Deși variabila din corpul funcției lambda este o copie, valoarea ei nu poate fi modificată în interiorul corpului de acum. Pentru a realiza captarea după valoare, fiecare variabilă repetată în clauza de captare nu este precedată de nimic. Următorul program ilustrează acest lucru:

#include

folosind spațiu de numeore;

intprincipal()

{

intid= 5; plutift= 2.3; charcap= 'LA';

autofn= [id, ft, cap]()

{

// id = 6; ft = 3,4; ch = „B”;

cost <<id<< ',' <<ft<< ',' <<cap<< ' n';

};

fn();

id= 6;ft= 3.4;cap= „B”;

cost <<id<< ',' <<ft<< ',' <<cap<< ' n';

întoarcere 0;

}

Ieșirea este:

5, 2.3, A

6, 3.4, B

Dacă indicatorul de comentariu este eliminat, programul nu se va compila. Compilatorul va emite un mesaj de eroare că variabilele din definiția corpului funcției pentru expresia lambda nu pot fi modificate. Deși variabilele nu pot fi modificate în interiorul funcției lambda, ele pot fi schimbate în afara funcției lambda, așa cum arată ieșirea programului de mai sus.

Amestecând capturi

Capturarea prin referință și capturarea după valoare pot fi amestecate, după cum arată următorul program:

#include

folosind spațiu de numeore;

intprincipal()

{

intid= 5; plutift= 2.3; charcap= 'LA'; boolbl= Adevărat;


autofn= [id, ft,&ch,&bl]()

{

cap= „B”;bl= fals;

cost <<id<< ',' <<ft<< ',' <<cap<< ',' <<bl<< ' n';

};

fn();


întoarcere 0;

}

Ieșirea este:

5, 2.3, B, 0

Când toate sunt capturate, sunt prin referință:

Dacă toate variabilele care urmează a fi capturate sunt capturate prin referință, atunci doar una și va fi suficientă în clauza de captare. Următorul program ilustrează acest lucru:

#include

folosind spațiu de numeore;

intprincipal()

{

intid= 5; plutift= 2.3; charcap= 'LA'; boolbl= Adevărat;


autofn= [&]()

{

id= 6;ft= 3.4;cap= „B”;bl= fals;

};

fn();

cost <<id<< ',' <<ft<< ',' <<cap<< ',' <<bl<< ' n';


întoarcere 0;

}

Ieșirea este:

6, 3.4, B, 0

Dacă unele variabile trebuie capturate prin referință și altele prin valoare, atunci una și va reprezenta toate referințele, iar restul nu vor fi precedate de nimic, așa cum arată următorul program:

folosind spațiu de numeore;

intprincipal()

{

intid= 5; plutift= 2.3; charcap= 'LA'; boolbl= Adevărat;


autofn= [&, id, ft]()

{

cap= „B”;bl= fals;

cost <<id<< ',' <<ft<< ',' <<cap<< ',' <<bl<< ' n';

};

fn();


întoarcere 0;

}

Ieșirea este:

5, 2.3, B, 0

Rețineți că & singur (adică, & nu urmat de un identificator) trebuie să fie primul caracter din clauza de captură.

Când toate sunt capturate, sunt în funcție de valoare:

Dacă toate variabilele care trebuie capturate urmează să fie capturate după valoare, atunci doar una = va fi suficientă în clauza de captare. Următorul program ilustrează acest lucru:

#include

folosind spațiu de numeore;

intprincipal()
{

intid= 5; plutift= 2.3; charcap= 'LA'; boolbl= Adevărat;


autofn= [=]()

{

cost <<id<< ',' <<ft<< ',' <<cap<< ',' <<bl<< ' n';

};

fn();


întoarcere 0;


}

Ieșirea este:

5, 2.3, A, 1

Notă : = este doar în citire, de acum.

Dacă unele variabile urmează să fie capturate prin valoare și altele prin referință, atunci one = va reprezenta toate variabilele copiate numai în citire, iar restul vor avea fiecare &, așa cum arată următorul program:

#include

folosind spațiu de numeore;

intprincipal()

{

intid= 5; plutift= 2.3; charcap= 'LA'; boolbl= Adevărat;


autofn= [=,&ch,&bl]()

{

cap= „B”;bl= fals;

cost <<id<< ',' <<ft<< ',' <<cap<< ',' <<bl<< ' n';

};

fn();


întoarcere 0;

}

Ieșirea este:

5, 2.3, B, 0

Rețineți că = singur trebuie să fie primul caracter din clauza de captură.

Schemă funcțională de apelare clasică cu expresie Lambda

Următorul program arată cum se poate realiza o schemă clasică de funcții de apelare cu expresia lambda:

#include

folosind spațiu de numeore;

char *ieșire;


autocba= [](charafară[])

{

ieșire=afară;

};



nulmainFunc(charintrare[],nul (*pt)(char[]))

{

(*pt)(intrare);

cost<<„pentru funcția principală”<<' n';

}


nulfn()

{

cost<<'Acum'<<' n';

}


intprincipal()

{

charintrare[] = „pentru funcția de apel invers”;

mainFunc(intrare, cba);

fn();

cost<<ieșire<<' n';



întoarcere 0;

}

Ieșirea este:

pentru funcția principală

Acum

pentru funcția de apel invers

Amintiți-vă că atunci când o definiție a expresiei lambda este atribuită unei variabile din sfera globală, corpul funcției sale poate vedea variabile globale fără a utiliza clauza de captură.

Tipul de întoarcere-returnare

Tipul returnat al unei expresii lambda este auto, ceea ce înseamnă că compilatorul determină tipul returnat din expresia returnată (dacă este prezentă). Dacă programatorul dorește cu adevărat să indice tipul de returnare, atunci îl va face ca în următorul program:

#include

folosind spațiu de numeore;

autofn= [](intStop) -> int

{

intRăspuns=Stop+ 3;

întoarcereRăspuns;

};


intprincipal()

{

autovariab=fn(2);

cost <<variab<< ' n';


întoarcere 0;

}

Ieșirea este 5. După lista parametrilor, se tastează operatorul săgeată. Acesta este urmat de tipul returnat (int în acest caz).

Închidere

Luați în considerare următorul segment de cod:

structCla

{

intid= 5;

charcap= 'la';

}obj1, obj2;

Aici, Cla este numele clasei struct. Obj1 și obj2 sunt două obiecte care vor fi instanțiate din clasa struct. Expresia Lambda este similară în implementare. Definiția funcției lambda este un fel de clasă. Când funcția lambda este numită (invocată), un obiect este instanțiat din definiția sa. Acest obiect se numește închidere. Închiderea este cea care face lucrarea pe care se așteaptă să o facă lambda.

Cu toate acestea, codificarea expresiei lambda ca structura de mai sus va avea obiect1 și obj2 înlocuite cu argumentele parametrilor corespunzători. Următorul program ilustrează acest lucru:

#include

folosind spațiu de numeore;

autofn= [](intparam1,intparam2)

{

intRăspuns=param1+param2;

întoarcereRăspuns;

} (2,3);


intprincipal()

{

autoUnde=fn;

cost <<Unde<< ' n';


întoarcere 0;

}

Rezultatul este 5. Argumentele sunt 2 și 3 între paranteze. Rețineți că apelul funcției de expresie lambda, fn, nu ia niciun argument, deoarece argumentele au fost deja codificate la sfârșitul definiției funcției lambda.

Concluzie

Expresia lambda este o funcție anonimă. Este în două părți: clasă și obiect. Definiția sa este un fel de clasă. Când se apelează expresia, din definiție se formează un obiect. Acest obiect se numește închidere. Închiderea este cea care face lucrarea pe care se așteaptă să o facă lambda.

Pentru ca expresia lambda să primească o variabilă dintr-un domeniu de funcție extern, are nevoie de o clauză de captură non-goală în corpul funcției sale.