lambda function
Főnév
lambda function (tsz. lambda functions)
- (informatika) A lambda függvények vagy lambda kifejezések a C++11 standardban jelentek meg, és a nyelv egyik legnagyobb újítása volt az utóbbi években. A lambda lehetővé teszi, hogy névtelen, inline definiált függvényeket hozzunk létre, amelyeket azonnal használhatunk, vagy akár továbbadhatunk más függvényeknek.
Egyszerűbben fogalmazva: a lambda egy olyan függvény, amit nem kell külön névvel ellátni, hanem közvetlenül a kód helyén definiálunk és használunk.
Miért hasznosak a lambda függvények?
- Egyszerűsítik a kódot: Nem kell külön függvényt írni minden apró művelethez.
- Kényelmesebbek a callback-ekhez: Például STL algoritmusokhoz, eseménykezeléshez.
- Zárják (capture-elik) a környező változókat: Így a lambda függvény hozzáfér a környező scope-ban lévő változókhoz, akár másolva, akár referencia szerint.
Lambda függvény alap szintaxisa
A legegyszerűbb lambda így néz ki:
[ capture ] ( parameters ) -> return_type {
// function body
};
Mindegyik elem magyarázata:
[ capture ]— a lambda „fogási lista” (capture list), azaz mely külső változókat kötünk be.( parameters )— a lambda bemeneti paraméterei (lehetnek üresek is).-> return_type— opcionális, megadható a visszatérési típus, ha a fordító nem tudná egyértelműen.{ ... }— maga a függvény törzse.
Példa 1: Egyszerű lambda függvény
auto hello = []() {
std::cout << "Hello, lambda!" << std::endl;
};
hello(); // Meghívás
Itt a hello egy névtelen függvény, amit a változóhoz rendelünk, majd meghívunk.
A capture list jelentése
A capture lista adja meg, hogy a lambda milyen külső változókat használhat a környezetből, és hogyan (másolat vagy referencia). Lehetőségek:
[ ]— semmilyen külső változó nem kerül be.[=]— minden külső változó másolatként bekerül.[&]— minden külső változó referencia szerint bekerül.[x, &y]—xváltozót másolatként,yváltozót referencia szerint kötünk be.this— a lambda belsejében az aktuális osztályobjektum pointere érhető el.
Példa 2: Capture lista használata
int a = 10;
int b = 20;
auto sum_by_value = [=]() {
return a + b; // másolatban használja a és b értékét
};
auto sum_by_ref = [&]() {
return a + b; // referencia szerint használja a és b-t
};
std::cout << sum_by_value() << std::endl; // 30
std::cout << sum_by_ref() << std::endl; // 30
a = 15;
b = 25;
std::cout << sum_by_value() << std::endl; // 30 (mert másolat)
std::cout << sum_by_ref() << std::endl; // 40 (mert referencia)
Paraméterek és visszatérési típus
A lambda paraméterei pontosan úgy működnek, mint egy normál függvénynél.
A visszatérési típus megadható explicit módon az -> szintaxissal, vagy implicit, ha a fordító ki tudja következtetni.
Példa 3: Paraméterezett lambda
auto add = [](int x, int y) -> int {
return x + y;
};
std::cout << add(3, 4) << std::endl; // 7
Példa 4: Implicit visszatérési típus
Ha a visszatérési típus egyszerű, elhagyható:
auto multiply = [](int x, int y) {
return x * y; // a fordító int-nek veszi
};
std::cout << multiply(5, 6) << std::endl; // 30
Példa 5: Több utasításos lambda
Ha több utasítást akarunk végrehajtani:
auto greet = [](std::string name) {
std::cout << "Hello, " << name << "!" << std::endl;
};
greet("Dániel");
A lambda mint függvényobjektum (funktor)
A lambda nem sima függvény, hanem egy névtelen osztály egy példánya, amely operator()-t definiál.
Ezért:
- Lambda típusának nincs konkrét neve, mindig
auto-val kell tárolni. - Lambda átküldhető olyan helyekre, ahol függvényobjektumot várnak (pl. STL algoritmusok).
STL és lambda függvények
A lambda kifejezések az STL algoritmusokkal nagyon hasznosak.
Példa 6: Rendezés lambda-val
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5, 9};
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b; // csökkenő sorrend
});
for (int n : v) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
Ez a példa lambda-t használ az egyéni rendezési feltételhez.
Példa 7: Szűrés és feldolgozás
std::vector<int> numbers = {1,2,3,4,5,6,7,8,9,10};
int count_even = 0;
std::for_each(numbers.begin(), numbers.end(), [&](int x){
if (x % 2 == 0) {
++count_even;
}
});
std::cout << "Páros számok száma: " << count_even << std::endl;
Mutable lambda
Alapértelmezés szerint, ha a capture másolat ([=]), akkor a lambda testén belül a captured változók nem módosíthatók, mert implicit const a operator().
Ha módosítani szeretnénk a capture-ölt másolatokat, használhatjuk a mutable kulcsszót.
Példa 8: Mutable lambda
int x = 10;
auto f = [x]() mutable {
x += 5;
std::cout << "Lambda belsejében x: " << x << std::endl;
};
f(); // kiírja: 15
std::cout << "Külső x: " << x << std::endl; // kiírja: 10 (külső változó nem változik)
A mutable lehetővé teszi, hogy a lambda a captured változó másolatát módosítsa, de nem változtatja meg a külső változót.
Visszatérés lambda függvényként (function returning lambda)
Lambdát visszaadó függvényt is írhatunk.
auto make_adder(int n) {
return [n](int x) {
return x + n;
};
}
int main() {
auto add5 = make_adder(5);
std::cout << add5(10) << std::endl; // 15
}
Típusjegyzék és std::function
Ha lambdát szeretnénk például eltárolni olyan változóban, ami kompatibilis bármilyen hasonló függvénnyel, használhatjuk az std::function-t.
#include <functional>
std::function<int(int,int)> func = [](int a, int b) {
return a + b;
};
std::cout << func(3,4) << std::endl; // 7
Az std::function típus viszont lassabb lehet, mert dinamikus diszpatchet (virtuális függvénytábla hívást) használ.
Capture-ök speciális esetek
thiscapture:
Osztályon belül lambda-t írva elérhetjük az objektum tagjait így:
class MyClass {
int val = 42;
public:
void foo() {
auto lam = [this]() {
std::cout << val << std::endl;
};
lam();
}
};
- Capture referencia szerint csak egy változóra:
int a = 10;
auto f = [&a]() { a += 5; };
f();
std::cout << a << std::endl; // 15
Több soros, komplex lambdák
Lambda-n belül bármennyi sor lehet:
auto complex = [](int x) {
if (x > 0) {
std::cout << "Pozitív" << std::endl;
} else {
std::cout << "Nem pozitív" << std::endl;
}
};
Lambda a ciklusokban
Fontos, hogy ha cikluson belül hozunk létre lambdákat, akkor a capture-nek meg kell felelnie annak, amit elvárunk. Például:
std::vector<std::function<void()>> funcs;
for (int i = 0; i < 3; ++i) {
funcs.push_back([i]() { std::cout << i << std::endl; });
}
for (auto& f : funcs) {
f(); // 0 1 2 kiíródik
}
Ha nem capture-eljük i-t értékként, hanem referenciaként, a lambda mindhárom esetben ugyanazt a i változót nézné, ami a ciklus után 3 lesz, tehát mindhárom lambda 3-at írna ki.
Lambda összefoglaló – előnyök és hátrányok
Előnyök:
- Egyszerű és tömör függvények helyben.
- Könnyű használat STL algoritmusokkal.
- Capture segítségével könnyen hozzáférnek a külső környezethez.
- Funkcionális programozási elemek bevezetése C++-ba.
Hátrányok:
- Típusuk nem névvel rendelkező, csak
autovagystd::functionhasználható. - Ha capture referencia szerint, vigyázni kell az élettartamra.
std::functionhasználata lassabb lehet.
Összefoglalás
- A lambda egy névtelen függvény, amit inline definiálunk.
- Capture listában megadjuk, mely környező változókat használ a lambda.
- STL algoritmusokban és callback-ekben nagyon praktikusak.
- Támogatják a paramétereket és visszatérési típust.
mutablekulcsszó lehetővé teszi a másolatként capture-ölt változók módosítását.- A lambda típusa névtelen, ezért
autovagystd::functionkell a tárolásához.
- lambda function - Szótár.net (en-hu)
- lambda function - Sztaki (en-hu)
- lambda function - Merriam–Webster
- lambda function - Cambridge
- lambda function - WordNet
- lambda function - Яндекс (en-ru)
- lambda function - Google (en-hu)
- lambda function - Wikidata
- lambda function - Wikipédia (angol)