Wyrażenia Lambda w C ++

Wyrażenia Lambda w C ++

Dlaczego wyrażenie lambda?

Rozważ następujące stwierdzenie:

int myint = 52;

Tutaj myint jest identyfikatorem, lValue. 52 to dosłowne, pralela. Dziś możliwe jest specjalnie kodowanie funkcji i umieszczenie jej w pozycji 52. Taka funkcja nazywa się wyrażeniem lambda. Rozważ także następujący krótki program:

#włączać
za pomocą przestrzeni nazw Std;
int fn (int par)

int Odpowiedź = par + 3;
Zwróć odpowiedź;

int main ()

fn (5);
powrót 0;

Dziś możliwe jest specjalnie zakodowanie funkcji i umieszczenie jej w pozycji argumentu 5, wywołania funkcji, FN (5). Taka funkcja nazywa się wyrażeniem lambda. Wyrażenie Lambda (funkcja) w tej pozycji jest pralelem.

Wszelkie dosłowne oprócz literału sznurka jest pralelem. Wyrażenie Lambda to specjalny projekt funkcji, który pasowałby do kodu jako dosłownego. Jest to funkcja anonimowa (nienazwana). W tym artykule wyjaśniono nowe wyrażenie pierwotne C ++, zwane ekspresją Lambda. Podstawowa wiedza w C ++ jest wymogiem zrozumienia tego artykułu.

Treść artykułu

  • Ilustracja ekspresji Lambda
  • Części ekspresji Lambda
  • Przechwyty
  • Klasyczny schemat funkcji zwrotnej z wyrażeniem Lambda
  • Typ zwrotny
  • Zamknięcie
  • Wniosek

Ilustracja ekspresji Lambda

W poniższym programie funkcja, która jest wyrażeniem Lambda, jest przypisana do zmiennej:

#włączać
za pomocą przestrzeni nazw Std;
auto fn = [] (int param)

int Odpowiedź = param + 3;
Zwróć odpowiedź;
;
int main ()

Auto variab = fn (2);
Cout << variab << '\n';
powrót 0;

Wyjście to:

5

Poza funkcją main () istnieje zmienna, FN. Jego typ jest automatyczny. Auto w tej sytuacji oznacza, że ​​rzeczywisty typ, taki jak int lub float, jest określany przez odpowiedni operand operatora przypisania (=). Po prawej stronie operatora przypisania jest wyrażenie lambda. Wyrażenie lambda jest funkcją bez poprzedniego typu powrotu. Zwróć uwagę na użycie i pozycję kwadratowych nawiasów, []. Funkcja zwraca 5, INT, która określi typ FN.

W funkcji Main () istnieje instrukcja:

Auto variab = fn (2);

Oznacza to, że FN Outside Main () kończy się jako identyfikator funkcji. Jego niejawne parametry to parametry wyrażenia Lambda. Typ Variab to automatyczne.

Zauważ, że wyrażenie lambda kończy się półkolisem, podobnie jak definicja klasy lub struktury, kończy się półkolisem.

W poniższym programie funkcja, która jest wyrażeniem lambda zwracającym wartość 5, jest argumentem innej funkcji:

#włączać
za pomocą przestrzeni nazw Std;
void inateFn (int no1, int (*ptr) (int))

int no2 = (*ptr) (2);
Cout << no1 << " << no2 << '\n';

int main ()

Innefn (4, [] (int param)

int Odpowiedź = param + 3;
Zwróć odpowiedź;
);
powrót 0;

Wyjście to:

4 5

Istnieją tutaj dwie funkcje, wyrażenie lambda i funkcja innafn (). Wyrażenie Lambda jest drugim argumentem drugiegofn (), zwanego w Main (). Należy zauważyć, że funkcja Lambda (wyrażenie) nie kończy się na półkolisie w tym wywołaniu, ponieważ tutaj jest argumentem (nie funkcja samodzielna).

Parametr funkcji Lambda w definicji funkcji drugiej Fn () jest wskaźnikiem do funkcji. Wskaźnik ma nazwę, ptr. Nazwa, ptr, jest używana w definicji Otherfn (), aby wywołać funkcję Lambda.

Twierdzenie,

int no2 = (*ptr) (2);

W drugiej definicji () wywołuje funkcję lambda z argumentem 2. Wartość zwracana wywołania „(*ptr) (2)” z funkcji Lambda jest przypisywana do NO2.

Powyższy program pokazuje również, w jaki sposób funkcję Lambda może być używana w schemacie funkcji zwrotnej C ++.

Części ekspresji Lambda

Części typowej funkcji Lambda są następujące:

[] ()
  • [] to klauzula przechwytywania. Może mieć przedmioty.
  • () dotyczy listy parametrów.
  • dotyczy ciała funkcyjnego. Jeśli funkcja stoi sama, powinna zakończyć się półkolisem.

Przechwyty

Definicję funkcji Lambda można przypisać do zmiennej lub użyć jako argument do innego wywołania funkcji. Definicja takiego wywołania funkcji powinna mieć jako parametr, wskaźnik do funkcji, odpowiadający definicji funkcji Lambda.

Definicja funkcji Lambda różni się od normalnej definicji funkcji. Można go przypisać do zmiennej w globalnym zakresie; ten przypisany do funkcji do zmienności można również zakodować w innej funkcji. Przy przypisaniu globalnej zmiennej zakresu, jego ciało może zobaczyć inne zmienne w zakresie globalnego. Przy przypisaniu do zmiennej wewnątrz normalnej definicji funkcji, jego ciało może zobaczyć inne zmienne w zakresie funkcji tylko z pomocą klauzuli przechwytywania, [].

Klauzula przechwytywania [], znana również jako Lambda-Introducer, umożliwia wysyłanie zmiennych z otaczającego (funkcyjnego) zakresu do ciała funkcyjnego ekspresji Lambda. Mówi się, że ciało funkcyjne wyrażenia Lambda przechwytuje zmienną, gdy odbiera obiekt. Bez klauzuli przechwytywania [] zmiennej nie można wysłać z otaczającego zakresu do ciała funkcyjnego wyrażenia Lambda. Poniższy program ilustruje to z zakresem funkcji Main (), jako otaczającego zakresu:

#włączać
za pomocą przestrzeni nazw Std;
int main ()

int id = 5;
auto fn = [id] ()

Cout << id << '\n';
;
fn ();
powrót 0;

Wyjście jest 5. Bez nazwy, id, wewnątrz [] wyrażenie lambda nie widziałoby zmiennego identyfikatora funkcji main (.

Przechwytywanie przez odniesienie

Powyższe przykładowe użycie klauzuli przechwytywania jest przechwytywane według wartości (patrz szczegóły poniżej). Podczas przechwytywania przez odniesienie, lokalizacja (przechowywanie) zmiennej, e.G., ID powyżej, otaczającego zakresu, jest udostępniany w korpusie funkcji Lambda. Zatem zmiana wartości zmiennej wewnątrz korpusu funkcji Lambda zmieni wartość tej samej zmiennej w otaczającym zakresie. Każda zmienna powtarzana w klauzuli przechwytywania jest poprzedzona przez Ampersand (&), aby to osiągnąć. Poniższy program ilustruje to:

#włączać
za pomocą przestrzeni nazw Std;
int main ()

int id = 5; float ft = 2.3; char ch = „a”;
auto fn = [& id, & ft i ch] ()

id = 6; ft = 3.4; CH = „B”;
;
fn ();
Cout << id << ", " << ft << ", " << ch << '\n';
powrót 0;

Wyjście to:

6, 3.4, ur

Potwierdzając, że nazwy zmiennych wewnątrz ciała funkcyjnego wyrażenia Lambda są dla tych samych zmiennych poza wyrażeniem Lambda.

Przechwytywanie według wartości

Podczas przechwytywania wartości, kopia lokalizacji zmiennej, otaczającego zakresu, jest udostępniana w korpusie funkcji Lambda. Chociaż zmienna wewnątrz ciała funkcyjnego Lambda jest kopią, jego wartości nie można zmienić w ciele. Aby osiągnąć przechwytywanie według wartości, każda zmienna powtarzana w klauzuli przechwytywania nie jest poprzedzona niczym. Poniższy program ilustruje to:

#włączać
za pomocą przestrzeni nazw Std;
int main ()

int id = 5; float ft = 2.3; char ch = „a”;
auto fn = [id, ft, ch] ()

// id = 6; ft = 3.4; CH = „B”;
Cout << id << ", " << ft << ", " << ch << '\n';
;
fn ();
id = 6; ft = 3.4; CH = „B”;
Cout << id << ", " << ft << ", " << ch << '\n';
powrót 0;

Wyjście to:

5, 2.3, a
6, 3.4, ur

Jeśli wskaźnik komentarza zostanie usunięty, program nie zostanie skompilowany. Kompilatora wyda komunikat o błędzie, że zmienne wewnątrz definicji wyrażenia Lambda Body Funkcja nie można zmienić. Chociaż zmiennych nie można zmienić w funkcji Lambda, można je zmienić poza funkcją Lambda, jak pokazuje powyższe wyniki programu.

Mieszanie przechwytywania

Schwytanie przez odniesienie i przechwytywanie według wartości można mieszać, jak pokazuje następujący program:

#włączać
za pomocą przestrzeni nazw Std;
int main ()

int id = 5; float ft = 2.3; char ch = „a”; bool Bl = true;
auto fn = [id, ft i ch, i bl] ()

CH = „B”; BL = Fałsz;
Cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
powrót 0;

Wyjście to:

5, 2.3, B, 0

Gdy wszystkie schwytane są w odniesieniu:

Jeśli wszystkie zmienne, które zostaną schwytane, zostaną schwytane przez odniesienie, to tylko jeden i wystarczy w klauzuli przechwytywania. Poniższy program ilustruje to:

#włączać
za pomocą przestrzeni nazw Std;
int main ()

int id = 5; float ft = 2.3; char ch = „a”; bool Bl = true;
auto fn = [&] ()

id = 6; ft = 3.4; CH = „B”; BL = Fałsz;
;
fn ();
Cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
powrót 0;

Wyjście to:

6, 3.4, b, 0

Jeśli niektóre zmienne mają zostać przechwycone przez odniesienie, a inne według wartości, to jeden i będzie reprezentował wszystkie odniesienia, a reszta nie będzie poprzedzona niczym, jak pokazuje następujący program:

#włączać
za pomocą przestrzeni nazw Std;
int main ()

int id = 5; float ft = 2.3; char ch = „a”; bool Bl = true;
auto fn = [&, id, ft] ()

CH = „B”; BL = Fałsz;
Cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
powrót 0;

Wyjście to:

5, 2.3, B, 0

Zwróć uwagę na to i sam (ja.mi., i nie za następującym identyfikatorem) musi być pierwszą postacią w klauzuli przechwytywania.

Gdy wszystkie są przechwycone, są według wartości:

Jeśli wszystkie zmienne, które mają zostać schwytane, mają zostać przechwycone według wartości, to tylko jeden = wystarczy w klauzuli przechwytywania. Poniższy program ilustruje to:

#włączać
za pomocą przestrzeni nazw Std;
int main ()

int id = 5; float ft = 2.3; char ch = „a”; bool Bl = true;
auto fn = [=] ()

Cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
powrót 0;

Wyjście to:

5, 2.3, a, 1

Notatka: = jest tylko do odczytu, jak teraz.

Jeśli niektóre zmienne mają zostać przechwycone przez wartość, a inne przez odniesienie, wówczas jeden = będzie reprezentował wszystkie zmienne skopiowane tylko do odczytu, a reszta będzie miała i, jak pokazuje następujący program:

#włączać
za pomocą przestrzeni nazw Std;
int main ()

int id = 5; float ft = 2.3; char ch = „a”; bool Bl = true;
auto fn = [= i ch, i bl] ()

CH = „B”; BL = Fałsz;
Cout << id << ", " << ft <<
"," << ch << ", " <<
Bl << '\n';
;
fn ();
powrót 0;

Wyjście to:

5, 2.3, B, 0

Zauważ, że sam = sam musi być pierwszą postacią w klauzuli przechwytywania.

Klasyczny schemat funkcji zwrotnej z wyrażeniem Lambda

Poniższy program pokazuje, w jaki sposób klasyczny schemat funkcji zwrotnej można wykonać za pomocą wyrażenia Lambda:

#włączać
za pomocą przestrzeni nazw Std;
Char *wyjście;
Auto CBA = [] (Char Out [])

wyjście = out;
;
void Principalfunc (char input [], void (*pt) (char []))

(*pt) (wejście);
Cout<<"for principal function"<<'\n';

void fn ()

Cout<<"Now"<<'\n';

int main ()

char input [] = "dla funkcji zwrotnej";
PrincipalFunc (Input, CBA);
fn ();
Cout<powrót 0;

Wyjście to:

dla głównej funkcji
Teraz
dla funkcji wywołania zwrotnego

Przypomnij sobie, że gdy definicja wyrażenia Lambda jest przypisywana do zmiennej w zakresie globalnym, jej organ funkcyjny może zobaczyć zmienne globalne bez stosowania klauzuli przechwytywania.

Typ zwrotny

Rodzaj powrotu wyrażenia lambda to automatyczne, co oznacza, że ​​kompilator określa typ powrotu z wyrażenia powrotu (jeśli jest obecny). Jeśli programista naprawdę chce wskazać typ powrotu, zrobi to jak w następującym programie:

#włączać
za pomocą przestrzeni nazw Std;
auto fn = [] (int param) -> int

int Odpowiedź = param + 3;
Zwróć odpowiedź;
;
int main ()

Auto variab = fn (2);
Cout << variab << '\n';
powrót 0;

Wyjście to 5. Po liście parametrów wpisany jest operator strzałek. Następnie następuje typ powrotu (int w tym przypadku).

Zamknięcie

Rozważ następujący segment kodu:

struct cla

int id = 5;
char ch = „a”;
obj1, obj2;

Tutaj CLA to nazwa klasy struktury. Obj1 i obj2 to dwa obiekty, które zostaną utworzone z klasy struktury. Wyrażenie Lambda jest podobne w realizacji. Definicja funkcji Lambda jest rodzajem klasy. Gdy wywoływana jest funkcja Lambda (wywoływana), obiekt jest utworzony z jego definicji. Ten obiekt nazywa się zamknięciem. Jest to zamknięcie, które wykonuje praca, którą Lambda powinien wykonać.

Jednak kodowanie wyrażenia lambda, takiego jak powyższe struktura, będzie wymienić obj1 i obj2 argumentami odpowiednich parametrów. Poniższy program ilustruje to:

#włączać
za pomocą przestrzeni nazw Std;
auto fn = [] (int param1, int param2)

int Odpowiedź = param1 + param2;
Zwróć odpowiedź;
(2, 3);
int main ()

auto var = fn;
Cout << var << '\n';
powrót 0;

Wyjście to 5. Argumenty to 2 i 3 w nawiasach. Zauważ, że wywołanie funkcji wyrażenia lambda, FN, nie przyjmuje żadnych argumentów, ponieważ argumenty zostały już zakodowane na końcu definicji funkcji Lambda.

Wniosek

Wyrażenie Lambda jest funkcją anonimową. Jest w dwóch częściach: klasa i obiekt. Jego definicja jest rodzajem klasy. Kiedy wyrażenie jest wywoływane, z definicji powstaje obiekt. Ten obiekt nazywa się zamknięciem. Jest to zamknięcie, które wykonuje praca, którą Lambda powinien wykonać. Aby wyrażenie lambda mogło otrzymać zmienną z zewnętrznego zakresu funkcji, potrzebuje klauzuli o nieokreślonej przechwytywania w swoim ciele funkcyjnym.