Lambda izrazi v C ++

Lambda Expressions C



Zakaj Lambda Expression?

Razmislite o naslednji izjavi:

intmyInt= 52;

Tukaj je myInt identifikator, vrednost. 52 je dobesedno, prva vrednost. Danes je mogoče posebno kodirati funkcijo in jo postaviti v položaj 52. Takšna funkcija se imenuje lambda izraz. Upoštevajte tudi naslednji kratek program:







#vključi

z uporabo imenski prostorure;

intfn(intskozi)

{

intodgovor=skozi+ 3;

vrnitevodgovor;

}


intglavni()

{

fn(5);



vrnitev 0;

}

Danes je mogoče posebno kodirati funkcijo in jo postaviti v položaj argumenta 5, klica funkcije, fn (5). Takšna funkcija se imenuje lambda izraz. Lambda izraz (funkcija) v tem položaju je prva vrednost.



Vsak literal, razen literalnega niza, je prva vrednost. Lambda izraz je zasnova posebne funkcije, ki bi se kot koda ujemala z dobesedno. To je anonimna (neimenovana) funkcija. Ta članek razlaga nov primarni izraz C ++, imenovan lambda izraz. Osnovno znanje C ++ je pogoj za razumevanje tega članka.



Vsebina članka

Ilustracija lambda izraza

V naslednjem programu je spremenljivki dodeljena funkcija, ki je lambda izraz:





#vključi

z uporabo imenski prostorure;

samodejnofn= [](intustaviti)

{

intodgovor=ustaviti+ 3;

vrnitevodgovor;

};


intglavni()

{

samodejnovariab=fn(2);

stroški <<variab<< ' n';


vrnitev 0;

}

Izhod je:

5

Zunaj funkcije main () je spremenljivka fn. Njegov tip je avto. Samodejno v tem primeru pomeni, da je dejanski tip, na primer int ali float, določen z desnim operandom operaterja dodelitve (=). Na desni strani operatorja dodelitve je lambda izraz. Lambda izraz je funkcija brez predhodnega tipa vrnitve. Upoštevajte uporabo in položaj oglatih oklepajev, []. Funkcija vrne 5, int, ki določi vrsto za fn.



V funkciji main () je stavek:

samodejnovariab=fn(2);

To pomeni, da fn zunaj main () konča kot identifikator funkcije. Njegovi implicitni parametri so parametri lambda izraza. Vrsta variab je samodejna.

Upoštevajte, da se izraz lambda konča s podpičjem, tako kot definicija razreda ali strukture, s podpičjem.

V naslednjem programu je funkcija, ki je lambda izraz, ki vrača vrednost 5, argument drugi funkciji:

#vključi

z uporabo imenski prostorure;

ničnootherfn(intšt.1,int (*ptr)(int))

{

intšt.2= (*ptr)(2);

stroški <<št.1<< '' <<št.2<< ' n';

}


intglavni()

{

otherfn(4,[](intustaviti)

{

intodgovor=ustaviti+ 3;

vrnitevodgovor;

});


vrnitev 0;
}

Izhod je:

Štiri, pet

Tu sta dve funkciji, lambda izraz in funkcija otherfn (). Lambda izraz je drugi argument druge fn (), ki se kliče v main (). Upoštevajte, da se lambda funkcija (izraz) v tem klicu ne konča s podpičjem, ker je tukaj argument (ne samostojna funkcija).

Parameter lambda funkcije v definiciji funkcije otherfn () je kazalec na funkcijo. Kazalec ima ime, ptr. Ime ptr se uporablja v definiciji otherfn () za klicanje lambda funkcije.

Izjava,

intšt.2= (*ptr)(2);

V definiciji otherfn () kliče lambda funkcijo z argumentom 2. Vrnjena vrednost klica '(*ptr) (2)' iz lambda funkcije je dodeljena številki 2.

Zgornji program prikazuje tudi, kako je mogoče lambda funkcijo uporabiti v shemi funkcij povratnega klica C ++.

Deli lambda izraza

Tipična funkcija lambda je naslednja:

[] () {}
  • [] je klavzula zajemanja. Lahko ima predmete.
  • () je za seznam parametrov.
  • {} je za telo funkcije. Če funkcija stoji sama, se mora končati s podpičjem.

Zajema

Definicijo lambda funkcije lahko dodelite spremenljivki ali uporabite kot argument za klic druge funkcije. Opredelitev za tak klic funkcije mora imeti kot parameter kazalec na funkcijo, ki ustreza definiciji lambda funkcije.

Opredelitev lambda funkcije se razlikuje od običajne definicije funkcije. Lahko se dodeli spremenljivki v globalnem obsegu; to funkcijo, dodeljeno spremenljivki, je mogoče kodirati tudi znotraj druge funkcije. Ko je dodeljeno spremenljivki globalnega obsega, lahko njeno telo vidi druge spremenljivke v globalnem obsegu. Ko je dodeljeno spremenljivki znotraj običajne definicije funkcije, lahko njeno telo vidi druge spremenljivke v obsegu funkcije le s pomočjo klavzule zajema, [].

Klavzula zajema [], znana tudi kot lambda-uvodnik, omogoča pošiljanje spremenljivk iz okolice (funkcije) v telo funkcije lambda izraza. Funkcijsko telo lambda izraza naj bi sprejelo spremenljivko, ko prejme predmet. Brez klavzule zajema [] spremenljivke ni mogoče poslati iz okoliškega obsega v telo funkcije lambda izraza. Naslednji program ponazarja to z obsegom funkcije main () kot okolico:

#vključi

z uporabo imenski prostorure;

intglavni()

{

intid= 5;


samodejnofn= [id]()

{

stroški <<id<< ' n';

};

fn();


vrnitev 0;

}

Izhod je 5 . Brez imena, id, znotraj [], lambda izraz ne bi videl spremenljivke obsega funkcije main ().

Zajem po referenci

Zgornji primer uporabe klavzule zajemanja zajema po vrednosti (glej podrobnosti spodaj). Pri zajemanju z referenco je lokacija (shranjevanje) spremenljivke, npr. Id zgoraj, okoliškega obsega na voljo v telesu lambda funkcije. Tako bo sprememba vrednosti spremenljivke v telesu lambda funkcije spremenila vrednost te iste spremenljivke v okolici. Za dosego tega je pred vsako spremenljivko, ponovljeno v klavzuli o zajemanju, znak ampersand (&). Naslednji program to ponazarja:

#vključi

z uporabo imenski prostorure;

intglavni()

{

intid= 5; plavatift= 2.3; charpogl= 'TO';

samodejnofn= [&id,&ft,&pogl]()

{

id= 6;ft= 3.4;pogl= 'B';

};

fn();

stroški <<id<< ',' <<ft<< ',' <<pogl<< ' n';

vrnitev 0;

}

Izhod je:

6, 3.4, B

Potrditev, da so imena spremenljivk v telesu funkcije lambda izraza za iste spremenljivke zunaj lambda izraza.

Zajem po vrednosti

Pri zajemanju po vrednosti je v telesu lambda funkcije na voljo kopija lokacije spremenljivke in okolice. Čeprav je spremenljivka v telesu lambda funkcije kopija, njene vrednosti v telesu trenutno ni mogoče spreminjati. Da bi dosegli zajem po vrednosti, pred vsako spremenljivko, ponovljeno v klavzuli zajema, nič ne sledi. Naslednji program to ponazarja:

#vključi

z uporabo imenski prostorure;

intglavni()

{

intid= 5; plavatift= 2.3; charpogl= 'TO';

samodejnofn= [id, ft, ch]()

{

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

stroški <<id<< ',' <<ft<< ',' <<pogl<< ' n';

};

fn();

id= 6;ft= 3.4;pogl= 'B';

stroški <<id<< ',' <<ft<< ',' <<pogl<< ' n';

vrnitev 0;

}

Izhod je:

5, 2.3, A

6, 3.4, B

Če indikator komentarja odstranite, se program ne bo prevedel. Prevajalnik bo izdal sporočilo o napaki, da spremenljivk v definiciji lambda izraza telesa funkcije ni mogoče spremeniti. Čeprav spremenljivk ni mogoče spremeniti znotraj lambda funkcije, jih je mogoče spremeniti zunaj lambda funkcije, kot kaže rezultat zgornjega programa.

Mešanje posnetkov

Zajem po referenci in zajem po vrednosti se lahko mešata, kot prikazuje naslednji program:

#vključi

z uporabo imenski prostorure;

intglavni()

{

intid= 5; plavatift= 2.3; charpogl= 'TO'; boolbl= prav;


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

{

pogl= 'B';bl= napačno;

stroški <<id<< ',' <<ft<< ',' <<pogl<< ',' <<bl<< ' n';

};

fn();


vrnitev 0;

}

Izhod je:

5, 2,3, B, 0

Ko so vse zajete, so sklicevanja:

Če so vse spremenljivke, ki jih je treba zajeti, zajete s sklicem, bo v klavzuli zajema dovolj le ena &. Naslednji program to ponazarja:

#vključi

z uporabo imenski prostorure;

intglavni()

{

intid= 5; plavatift= 2.3; charpogl= 'TO'; boolbl= prav;


samodejnofn= [&]()

{

id= 6;ft= 3.4;pogl= 'B';bl= napačno;

};

fn();

stroški <<id<< ',' <<ft<< ',' <<pogl<< ',' <<bl<< ' n';


vrnitev 0;

}

Izhod je:

6, 3,4, B, 0

Če je treba nekatere spremenljivke zajeti s sklicem, druge pa z vrednostjo, bo ena & predstavljala vse reference, preostalim pa ne bo sledilo ničesar, kot kaže naslednji program:

z uporabo imenski prostorure;

intglavni()

{

intid= 5; plavatift= 2.3; charpogl= 'TO'; boolbl= prav;


samodejnofn= [&, id, ft]()

{

pogl= 'B';bl= napačno;

stroški <<id<< ',' <<ft<< ',' <<pogl<< ',' <<bl<< ' n';

};

fn();


vrnitev 0;

}

Izhod je:

5, 2,3, B, 0

Upoštevajte, da mora biti & sam (tj. & Ne sledi identifikator) prvi znak v klavzuli o zajemanju.

Ko so vsi zajeti, so po vrednosti:

Če je treba vse spremenljivke, ki jih je treba zajeti, zajeti z vrednostjo, bo v klavzuli zajema dovolj le ena =. Naslednji program to ponazarja:

#vključi

z uporabo imenski prostorure;

intglavni()
{

intid= 5; plavatift= 2.3; charpogl= 'TO'; boolbl= prav;


samodejnofn= [=]()

{

stroški <<id<< ',' <<ft<< ',' <<pogl<< ',' <<bl<< ' n';

};

fn();


vrnitev 0;


}

Izhod je:

5, 2.3, A, 1

Opomba : = je trenutno samo za branje.

Če je treba nekatere spremenljivke zajeti z vrednostjo, druge pa s sklicem, bo ena = predstavljala vse spremenjene spremenljivke samo za branje, ostale pa bodo imele &, kot kaže naslednji program:

#vključi

z uporabo imenski prostorure;

intglavni()

{

intid= 5; plavatift= 2.3; charpogl= 'TO'; boolbl= prav;


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

{

pogl= 'B';bl= napačno;

stroški <<id<< ',' <<ft<< ',' <<pogl<< ',' <<bl<< ' n';

};

fn();


vrnitev 0;

}

Izhod je:

5, 2,3, B, 0

Upoštevajte, da mora = sam biti prvi znak v klavzuli zajema.

Klasična shema funkcij povratnega klica z lambda izrazom

Naslednji program prikazuje, kako je mogoče z lambda izrazom narediti klasično shemo funkcij povratnega klica:

#vključi

z uporabo imenski prostorure;

char *izhod;


samodejnocba= [](charven[])

{

izhod=ven;

};



ničnomainFunc(charvnos[],nično (*za)(char[]))

{

(*za)(vnos);

stroški<<'za glavno funkcijo'<<' n';

}


ničnofn()

{

stroški<<'Zdaj'<<' n';

}


intglavni()

{

charvnos[] = 'za funkcijo povratnega klica';

mainFunc(vhod, cba);

fn();

stroški<<izhod<<' n';



vrnitev 0;

}

Izhod je:

za glavno funkcijo

Zdaj

za funkcijo povratnega klica

Spomnite se, da ko je definicija lambda izraza dodeljena spremenljivki v globalnem obsegu, lahko njeno telo funkcije vidi globalne spremenljivke, ne da bi uporabila klavzulo zajema.

Zadnji tip vrnitve

Vrsta vrnitve lambda izraza je samodejna, kar pomeni, da prevajalnik določi vrsto vrnitve iz izraza za vrnitev (če je prisoten). Če programer resnično želi navesti vrsto vračila, bo to storil kot v naslednjem programu:

#vključi

z uporabo imenski prostorure;

samodejnofn= [](intustaviti) -> int

{

intodgovor=ustaviti+ 3;

vrnitevodgovor;

};


intglavni()

{

samodejnovariab=fn(2);

stroški <<variab<< ' n';


vrnitev 0;

}

Izhod je 5. Po seznamu parametrov se vnese operator puščice. Temu sledi vrsta vrnitve (v tem primeru int).

Zaključek

Razmislite o naslednjem segmentu kode:

structCla

{

intid= 5;

charpogl= 'do';

}obj1, obj2;

Tukaj je Cla ime razreda struct. Obj1 in obj2 sta dva objekta, ki bosta ustvarjena iz razreda struct. Lambda izraz je pri izvajanju podoben. Opredelitev lambda funkcije je neke vrste razred. Ko je lambda funkcija poklicana (priklicana), se iz njene definicije pojavi primerek objekta. Ta objekt se imenuje zapiranje. Zapiranje je tisto, kar naj bi lambda opravila.

Vendar bo kodiranje lambda izraza, kot je zgornja struktura, zamenjalo obj1 in obj2 z argumenti ustreznih parametrov. Naslednji program to ponazarja:

#vključi

z uporabo imenski prostorure;

samodejnofn= [](intparam1,intparam2)

{

intodgovor=odstavek 1+param2;

vrnitevodgovor;

} (2,3);


intglavni()

{

samodejnokje=fn;

stroški <<kje<< ' n';


vrnitev 0;

}

Izhod je 5. Argumenta sta 2 in 3 v oklepajih. Upoštevajte, da klic funkcije lambda izraza, fn, ne sprejme nobenega argumenta, saj so bili argumenti že kodirani na koncu definicije lambda funkcije.

Zaključek

Lambda izraz je anonimna funkcija. Sestavljen je iz dveh delov: razreda in predmeta. Njegova opredelitev je nekakšen razred. Ko se izraz pokliče, iz definicije nastane predmet. Ta objekt se imenuje zapiranje. Zapiranje je tisto, kar naj bi lambda opravila.

Če želi lambda izraz sprejeti spremenljivko iz zunanjega obsega funkcije, potrebuje v svoje telo klavzulo o nepraznem zajemanju.