Wyrażenie lambda to jedno z pojęć w programowaniu, o którym jeszcze nieraz usłyszysz, zwłaszcza jak nabierzesz nieco doświadczenia! Opiszę Ci na czym to polega i, jak zawsze zresztą, zaprezentuję na prostym przykładzie. Wchodzisz w to 😄?
WYRAŻENIE LAMBDA W PROGRAMOWANIU FURTKĄ DO DEFINIOWANIA TREŚCI FUNKCJI "NA MIEJSCU"
Jak wiele różnych pojęć w informatyce, ten również pochodzi "z żebra" matematyki (konkretniej, to z rachunku lambda) i oznacza zdefiniowanie treści funkcji bezpośrednio w parametrze innej funkcji, która tego oczekuje. Z tego powodu otrzymało to jeszcze inną nazwę: funkcja anonimowa, ponieważ gdy osadzamy treść funkcji "na miejscu", nie podajemy wtedy żadnej nazwy, po której można by tę referencję odnaleźć (bo nie ma po co, jak jest "wszczepiona" na miejscu 😁).
Termin ma ścisły związek z funkcjami wyższego rzędu, tylko tutaj zamiast "przekazywania" wskaźnika do funkcji w miejsce parametru, definiujemy tę funkcję od razu. Taki "skrót" redukuje liczbę wierszy w kodzie źródłowym i o ile nie będziemy mieli wątpliwości, że ta sama definicja nie będzie wstawiona nigdzie indziej (mam na myśli reużywalność), taki zapis jak najbardziej będzie poprawny i elegancki ✅.
Przykład kodu źródłowego
Język C nie udostępnia możliwości osadzania ciała funkcji bezpośrednio w miejsce parametru (istnieją pewne triki z użyciem makr, jednak są zbyt hardkorowe, aby je pokazać jako przykład ⛔), więc przejdziemy do C++'a, w którym już istnieje taki zapis, a mianowicie "std::function". Spójrz na to:
#include <cctype>
#include <string>
#include <iostream>
#include <functional>
void doSomething(std::string string, std::function<int(std::string)> function);
int main()
{
auto string = "C Plus Plus";
doSomething(string, [](std::string string)
{
return string.length();
});
doSomething(string, [](std::string string)
{
auto numberOfOccurences = 0;
for (auto character : string)
{
if(tolower(character) == 'p')
{
++numberOfOccurences;
}
}
return numberOfOccurences;
});
return 0;
}
void doSomething(std::string string, std::function<int(std::string)> function)
{
if(function)
{
std::cout << function(string) << std::endl;
}
}Zaraz po dyrektywach, znajduje się prototyp funkcji, która będzie "wyzwalać" nasze wyrażenie lambda. Oprócz łańcucha znaków, przyjmuje też parametr w postaci funkcji (a dokładniej, wskaźnika do funkcji, co zostało ukazane podczas tłumaczenia funkcji wyższego rzędu).
W środku funkcji "main" definiujemy sobie łańcuch znaków, który zostanie dwukrotnie wprowadzony do funkcji "odpalającej" wyrażenie lambda. Celem zademonstrowania użyteczności wyrażeń lambda, dodałem DWA wywołania funkcji "doSomething" i zwróć uwagę, że i w pierwszym, i w drugim, mamy wplecione różne wyrażenia 😱!
Pierwsze z nich wyznaczy nam długość łańcucha podanego w parametrze, a drugie, liczbę wystąpień litery 'p'. Tu celowo podałem wywołanie funkcji "tolower" (wymaga dołączenia pliku "cctype"), aby program mógł potraktować małą i wielką literę 'P' jako to samo wystąpienie (bez tego by "przegapił" dwukrotne wystąpienie litery 'p'!).
Na samym dole mamy samą definicję funkcji, która nam "wyzwala" wyrażenie lambda. Oczywiście może się zdarzyć, że nic nie zostanie podane i wpiszemy złośliwie "NULL". Dlatego jest instrukcja warunkowa, która sprawdza wpierw czy została podana jakakolwiek definicja funkcji do wykonania. Jak tak, to wykonuje 😊.
Tytułem dygresji - "auto" w C++ pozwala przerzucić definiowanie typu zmiennej prosto na kompilator, który wnioskuje ten typ po podanej wartości.
Przeczytaj to!
Na sam koniec, wyjaśnijmy sobie ważną kwestię. W definicji funkcji wyższego rzędu, znajduje się wywołanie funkcji w parametrze. Kiedy ją wywołujemy (funkcję wyższego rzędu), to umieszczamy definicję funkcji do wykonania. Innymi słowy, "za kotarą" nie wygląda to tak:
doSomething(string, getLengthOfString());tylko tak 👇:
doSomething(string, getLengthOfString);W miejscu wywołania jest sama treść, a nie wywołanie (nazwa funkcji bez nawiasów)! Wywołanie tejże treści znajduje się tu:
std::cout << function(string) << std::endl;Czyli oddelegowujesz wykonanie tych instrukcji na później. Jest to bardzo ważne żebyś rozumiał(a), gdy zechcesz przejść na pisanie w ten sposób!
![]() |
Wyrażenie lambda do zdefiniowanie treści funkcji (oczekiwanej jako parametr funkcji wyższego rzędu) "wpleciony" w dane miejsce bezpośrednio. Określane jest jako "funkcja anonimowa", ponieważ nie wymaga żadnej nazwy, po której zwykle się identyfikuje daną funkcję, gdy jest zdefiniowana w innym miejscu w programie.
I z mojej strony to tyle. Dobrze wiedzieć takie smaczki, bo mogą bez kitu sprawić, że pisanie stanie się nie tylko przyjemniejsze, lecz także bardziej profesjonalne i po prostu będzie Ci szybciej kończyć pracę 🤩!
