Lambda väljendid C ++ keeles

Lambda Expressions C



Miks just Lambda Expression?

Mõelge järgmisele avaldusele:

intminuInt= 52;

Siin on myInt identifikaator, väärtus. 52 on sõnasõnaline, prvalue. Tänapäeval on võimalik funktsiooni spetsiaalselt kodeerida ja panna see asendisse 52. Sellist funktsiooni nimetatakse lambda -avaldiseks. Kaaluge ka järgmist lühiprogrammi:







#kaasake

kasutades nimeruumtundi;

intfn(intläbi)

{

intvastus=läbi+ 3;

tagasivastus;

}


intpeamine()

{

fn(5);



tagasi 0;

}

Tänapäeval on võimalik funktsiooni spetsiaalselt kodeerida ja panna see argumendi 5, funktsioonikõne fn (5) asendisse. Sellist funktsiooni nimetatakse lambda avaldiseks. Selles asendis olev lambda avaldis (funktsioon) on eelväärtus.



Iga literaal, välja arvatud string literal, on prvalue. Lambda avaldis on erifunktsiooni kujundus, mis sobiks koodis sõnasõnaliselt. See on anonüümne (nimetu) funktsioon. See artikkel selgitab uut C ++ esmast avaldist, mida nimetatakse lambda avaldiseks. Selle artikli mõistmiseks on nõutavad C ++ põhiteadmised.



Artikli sisu

Illustratsioon Lambda väljendist

Järgmises programmis määratakse muutujale funktsioon, mis on lambda -avaldis:





#kaasake

kasutades nimeruumtundi;

autofn= [](intpeatus)

{

intvastus=peatus+ 3;

tagasivastus;

};


intpeamine()

{

automuutuja=fn(2);

maksma <<muutuja<< ' n';


tagasi 0;

}

Väljund on:

5

Väljaspool põhifunktsiooni () on muutuja fn. Selle tüüp on automaatne. Automaatne selles olukorras tähendab, et tegeliku tüübi, näiteks int või float, määrab määramisoperaatori parem operand (=). Määramisoperaatori paremal on lambda -avaldis. Lambda -avaldis on funktsioon ilma eelneva tagasitüübita. Pange tähele nurksulgude kasutamist ja asukohta, []. Funktsioon tagastab 5, int, mis määrab fn tüübi.



Funktsioonis main () on lause:

automuutuja=fn(2);

See tähendab, et fn väljaspool main () saab funktsiooni identifikaatoriks. Selle kaudsed parameetrid on lambda avaldise parameetrid. Muutuja tüüp on automaatne.

Pange tähele, et lambda avaldis lõpeb semikooloniga, nagu klassi või struktuuri määratlus, lõpeb semikooloniga.

Järgmises programmis on funktsioon, mis on lambda -avaldis, mis tagastab väärtuse 5, argumendiks teisele funktsioonile:

#kaasake

kasutades nimeruumtundi;

tühinemuufn(intnr1,int (*ptr)(int))

{

intnr2= (*ptr)(2);

maksma <<nr1<< '' <<nr2<< ' n';

}


intpeamine()

{

muufn(4,[](intpeatus)

{

intvastus=peatus+ 3;

tagasivastus;

});


tagasi 0;
}

Väljund on:

Neli, viis

Siin on kaks funktsiooni, lambda avaldis ja funktsioon otherfn (). Lambda avaldis on teise argumendi teinefn (), nimega main (). Pange tähele, et lambda funktsioon (avaldis) ei lõpe selles kõnes semikooloniga, sest siin on see argument (mitte eraldiseisev funktsioon).

Funktsiooni lambda funktsiooni parameeter funktsiooni otherfn () määratluses on funktsiooni osutaja. Kursoril on nimi, ptr. Nime ptr kasutatakse definitsioonis otherfn () lambda -funktsiooni kutsumiseks.

Avaldus,

intnr2= (*ptr)(2);

Definitsioonis otherfn () kutsub see funktsiooni lambda üles argumendiga 2. Kõne tagastamisväärtus '(*ptr) (2)' lambda -funktsioonist on määratud nr2 -le.

Ülaltoodud programm näitab ka seda, kuidas lambda funktsiooni saab kasutada C ++ tagasihelistamise funktsiooni skeemis.

Lambda ekspressiooni osad

Tüüpilise lambda -funktsiooni osad on järgmised:

[] () {}
  • [] on püüdmisklausel. Sellel võib olla esemeid.
  • () on parameetrite loendi jaoks.
  • {} on funktsiooni keha jaoks. Kui funktsioon seisab üksi, peaks see lõppema semikooloniga.

Jäädvustab

Funktsiooni lambda määratluse saab määrata muutujale või kasutada argumendina mõnele muule funktsioonikõnele. Sellise funktsioonikõne definitsioonil peaks parameetrina olema funktsioonile osutaja, mis vastab lambda -funktsiooni definitsioonile.

Funktsiooni lambda määratlus erineb tavapärasest funktsiooni määratlusest. Selle saab määrata globaalse ulatuse muutujale; seda muutujale määratud funktsiooni saab kodeerida ka teise funktsiooni sees. Kui see on määratud globaalse ulatuse muutujale, näeb selle keha globaalse ulatuse teisi muutujaid. Kui see määratakse muutujale tavalise funktsiooni definitsiooni sees, näeb selle keha muid funktsioonide ulatuses olevaid muutujaid ainult hõiveklausli abil, [].

Pildistamisklausel [], tuntud ka kui lambda-sissejuhatus, võimaldab muutujaid saata ümbritsevast (funktsiooni) ulatusest lambda-avaldise funktsiooni kehasse. Väidetavalt haarab lambda -avaldise funktsiooni keha muutuja objekti vastuvõtmisel. Ilma püüdmisklauslita [] ei saa muutujat ümbritsevast ulatusest lambda -avaldise funktsiooni kehasse saata. Järgmine programm illustreerib seda koos põhifunktsiooni () ulatusega ümbritseva ulatusena:

#kaasake

kasutades nimeruumtundi;

intpeamine()

{

intid= 5;


autofn= [id]()

{

maksma <<id<< ' n';

};

fn();


tagasi 0;

}

Väljund on 5 . Ilma nimeta, id, [] sees poleks lambda -avaldis näinud funktsiooni () põhiaruumi muutuja id.

Pildistamine viite abil

Ülaltoodud näide hõivamisklauslist on väärtuse järgi jäädvustamine (vt üksikasju allpool). Viite abil jäädvustades tehakse muutuja asukoht (salvestusruum), nt ümbritsev ulatus id eespool, kättesaadavaks lambda funktsiooni korpuses. Niisiis, muutuja väärtuse muutmine lambda -funktsiooni korpuses muudab sama muutuja väärtust ümbritsevas ulatuses. Selle saavutamiseks eelneb igale muutmiskatsele korduvale muutujale märk & (). Seda illustreerib järgmine programm:

#kaasake

kasutades nimeruumtundi;

intpeamine()

{

intid= 5; hõljumajalga= 2.3; süsich= 'TO';

autofn= [&id,&jalga,&ch]()

{

id= 6;jalga= 3.4;ch= 'B';

};

fn();

maksma <<id<< ',' <<jalga<< ',' <<ch<< ' n';

tagasi 0;

}

Väljund on:

6, 3.4, B

Kinnitus, et muutujate nimed lambda -avaldise funktsioonikorpuses on samade muutujate jaoks väljaspool lambda -avaldist.

Väärtuse järgi jäädvustamine

Väärtuse järgi jäädvustades tehakse lambda funktsiooni korpuses kättesaadavaks koopia muutuja asukohast ja ümbritsevast ulatusest. Kuigi lambda -funktsiooni korpuses olev muutuja on koopia, ei saa selle väärtust keha sees praegu muuta. Väärtuse järgi jäädvustamise saavutamiseks ei eelne igale püüdmisklauslis korratud muutujale midagi. Seda illustreerib järgmine programm:

#kaasake

kasutades nimeruumtundi;

intpeamine()

{

intid= 5; hõljumajalga= 2.3; süsich= 'TO';

autofn= [id, ft, ch]()

{

// id = 6; ft = 3,4; ch = 'B';

maksma <<id<< ',' <<jalga<< ',' <<ch<< ' n';

};

fn();

id= 6;jalga= 3.4;ch= 'B';

maksma <<id<< ',' <<jalga<< ',' <<ch<< ' n';

tagasi 0;

}

Väljund on:

5, 2.3, A

6, 3.4, B

Kui kommentaarinäidik eemaldatakse, siis programm ei kompileeri. Kompilaator annab veateate, et muutujaid funktsiooni keha lambda -avaldise määratluses ei saa muuta. Kuigi muutujaid ei saa lambda funktsiooni sees muuta, saab neid muuta väljaspool lambda funktsiooni, nagu ülaltoodud programmi väljund näitab.

Pildistamise segamine

Viite järgi jäädvustamist ja väärtuse järgi jäädvustamist saab segada, nagu näitab järgmine programm:

#kaasake

kasutades nimeruumtundi;

intpeamine()

{

intid= 5; hõljumajalga= 2.3; süsich= 'TO'; boolbl= tõsi;


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

{

ch= 'B';bl= vale;

maksma <<id<< ',' <<jalga<< ',' <<ch<< ',' <<bl<< ' n';

};

fn();


tagasi 0;

}

Väljund on:

5, 2.3, B, 0

Kui kõik on jäädvustatud, viidatakse neile:

Kui kõik jäädvustatavad muutujad on jäädvustatud viitena, siis jäädvustamisklauslis piisab vaid ühest &. Seda illustreerib järgmine programm:

#kaasake

kasutades nimeruumtundi;

intpeamine()

{

intid= 5; hõljumajalga= 2.3; süsich= 'TO'; boolbl= tõsi;


autofn= [&]()

{

id= 6;jalga= 3.4;ch= 'B';bl= vale;

};

fn();

maksma <<id<< ',' <<jalga<< ',' <<ch<< ',' <<bl<< ' n';


tagasi 0;

}

Väljund on:

6, 3.4, B, 0

Kui mõnda muutujat tuleb tabada viite ja teisi väärtuse järgi, siis üks tähistab kõiki viiteid ja ülejäänud ei eelne igale, nagu näitab järgmine programm:

kasutades nimeruumtundi;

intpeamine()

{

intid= 5; hõljumajalga= 2.3; süsich= 'TO'; boolbl= tõsi;


autofn= [&, id, ft]()

{

ch= 'B';bl= vale;

maksma <<id<< ',' <<jalga<< ',' <<ch<< ',' <<bl<< ' n';

};

fn();


tagasi 0;

}

Väljund on:

5, 2.3, B, 0

Pange tähele, et & üksi (st., Millele ei järgne identifikaatorit) peab olema püüdmisklausli esimene märk.

Kui kõik on jäädvustatud, on need väärtuse järgi:

Kui kõik jäädvustatavad muutujad tuleb väärtuse järgi jäädvustada, piisab jäädvustamisklauslis vaid ühest =. Seda illustreerib järgmine programm:

#kaasake

kasutades nimeruumtundi;

intpeamine()
{

intid= 5; hõljumajalga= 2.3; süsich= 'TO'; boolbl= tõsi;


autofn= [=]()

{

maksma <<id<< ',' <<jalga<< ',' <<ch<< ',' <<bl<< ' n';

};

fn();


tagasi 0;


}

Väljund on:

5, 2.3, A, 1

Märge : = on praegu kirjutuskaitstud.

Kui mõnda muutujat tuleb tabada väärtuse ja teisi viite abil, siis üks = tähistab kõiki ainult kirjutuskaitstud kopeeritud muutujaid ja ülejäänutel on iga, nagu näitab järgmine programm:

#kaasake

kasutades nimeruumtundi;

intpeamine()

{

intid= 5; hõljumajalga= 2.3; süsich= 'TO'; boolbl= tõsi;


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

{

ch= 'B';bl= vale;

maksma <<id<< ',' <<jalga<< ',' <<ch<< ',' <<bl<< ' n';

};

fn();


tagasi 0;

}

Väljund on:

5, 2.3, B, 0

Pange tähele, et = üksi peab jäädvustamisklausli esimene märk olema.

Klassikaline tagasihelistamise funktsiooniskeem koos lambda avaldisega

Järgmine programm näitab, kuidas klassikalist tagasihelistamisfunktsiooni skeemi saab teha lambda -avaldisega:

#kaasake

kasutades nimeruumtundi;

süsi *väljund;


autocba= [](süsivälja[])

{

väljund=välja;

};



tühinemainFunc(süsisisend[],tühine (*eest)(süsi[]))

{

(*eest)(sisend);

maksma<<'põhifunktsiooni jaoks'<<' n';

}


tühinefn()

{

maksma<<'Nüüd'<<' n';

}


intpeamine()

{

süsisisend[] = 'tagasihelistamise funktsiooni jaoks';

mainFunc(sisend, cba);

fn();

maksma<<väljund<<' n';



tagasi 0;

}

Väljund on:

põhifunktsiooni jaoks

Nüüd

tagasihelistamise funktsiooni jaoks

Tuletame meelde, et kui lambda avaldise definitsioon on määratud globaalse ulatuse muutujale, näeb selle funktsiooni keha globaalseid muutujaid ilma hõivamisklauslit kasutamata.

Trailing-return-tüüp

Lambda -avaldise tagastustüüp on automaatne, mis tähendab, et kompilaator määrab tagastamisavaldise (kui see on olemas) tagastustüübi. Kui programmeerija soovib tõesti tagastustüüpi märkida, teeb ta seda järgmises programmis:

#kaasake

kasutades nimeruumtundi;

autofn= [](intpeatus) -> int

{

intvastus=peatus+ 3;

tagasivastus;

};


intpeamine()

{

automuutuja=fn(2);

maksma <<muutuja<< ' n';


tagasi 0;

}

Väljund on 5. Pärast parameetrite loendit sisestatakse nooleoperaator. Sellele järgneb tagastustüüp (antud juhul int).

Sulgemine

Mõelge järgmisele koodilõigule:

struktuuriCla

{

intid= 5;

süsich= '';

}obj1, obj2;

Siin on Cla struktuuristruktuuri nimi. Obj1 ja obj2 on kaks objekti, mis luuakse struktuuriklassist. Lambda avaldis on teostuses sarnane. Funktsiooni lambda määratlus on omamoodi klass. Kui lambda -funktsiooni kutsutakse (kutsutakse), luuakse objekt selle määratlusest. Seda objekti nimetatakse sulgemiseks. See on sulgemine, mis teeb tööd, mida lambda eeldatavalt teeb.

Kuid lambda avaldise kodeerimine nagu ülaltoodud struktuur asendab obj1 ja obj2 vastavate parameetrite argumentidega. Seda illustreerib järgmine programm:

#kaasake

kasutades nimeruumtundi;

autofn= [](intparam1,intparam2)

{

intvastus=param1+param2;

tagasivastus;

} (2,3);


intpeamine()

{

autokus=fn;

maksma <<kus<< ' n';


tagasi 0;

}

Väljund on 5. Argumendid on sulgudes 2 ja 3. Pange tähele, et lambda avaldisfunktsiooni kutsumine fn ei võta argumente, kuna argumendid on lambdafunktsiooni definitsiooni lõpus juba kodeeritud.

Järeldus

Lambda väljend on anonüümne funktsioon. See koosneb kahest osast: klass ja objekt. Selle määratlus on omamoodi klass. Kui avaldist kutsutakse, moodustatakse määratlusest objekt. Seda objekti nimetatakse sulgemiseks. See on sulgemine, mis teeb tööd, mida lambda eeldatavalt teeb.

Selleks, et lambda-avaldis saaks välise funktsiooni ulatusest muutuja, vajab see oma funktsiooni kehasse tühjenduslauset.