Les fonctions Lambda en C++
En C++, nous pouvons utiliser des fonctions sans nom (fonctions lambda). En fait, une telle fonction est un objet d'un type spécial, et cet objet peut être affecté à une variable. Après cela, nous pouvons appeler la variable comme s'il s'agissait d'une fonction.
Ainsi, une fonction lambda est une construction sémantique qui détermine une fonction. La syntaxe d'une fonction lambda est la suivante :
Syntaxe
[](parametres)->return_type{ // instructions }
La syntaxe commence par une paire de crochets [] (la clause de capture ou l'initialiseur lambda) suivie d'une parenthèse avec les paramètres de la fonction. Après la liste des paramètres, on met la flèche -> et l'identifiant du type de retour. Ensuite, dans les accolades, nous décrivons les instructions à exécuter lorsque la fonction est appelée.
- Si le compilateur peut déterminer le type de retour sur la base des instructions de la fonction uniquement, alors la flèche -> et l'identifiant du type de retour peuvent être omis. Lorsque la fonction lambda n'a pas de paramètres, nous pouvons également omettre la parenthèse après l'initialiseur lambda.
- Il est également à noter que dans l'initialiseur lambda, nous pouvons spécifier les noms de ces variables en dehors de la fonction lambda, qu'il utilise.
- Les fonctions lambda qui ne capturent pas les variables externes sont appelées sans état (stateless).
L'instruction décrivant une fonction lambda peut être affectée à une variable.
Le mot-clé auto définit le type de la variable. Cela signifie que le type de variable sera défini automatiquement en fonction de l'expression lambda affectée à la variable.
Une fonction lambda est un objet qui peut être appelé. L'objet appartient à un certain type, mais il n'a pas de nom. Nous ne pouvons donc pas spécifier le type explicitement. En d'autres termes, le type existe, mais nous ne connaissons pas son nom. C'est pourquoi nous utilisons le mot-clé auto.
Par exemple, l'instruction suivante affecte une fonction lambda à la variable somme :
Exemple 1
auto somme=[](int n)->int{ int s=0; for(int k=1;k<=n;k++){ s+=k; } return s; };
On définit ici la variable somme, qui se comporte comme s'il s'agissait d'une fonction.
La fonction lambda a un paramètre entier (défini comme n) et renvoie un entier comme résultat. La fonction a calculé la somme des nombres naturels de 1 à n. Ainsi, lorsque nous appelons la fonction avec un argument (comme dans l'instruction somme(7)), nous obtenons la somme des nombres naturels.
Exemple 2 : Un programme complet dans lequel nous utilisons des fonctions lambda.
#include <iostream> using namespace std; int main() { auto somme=[](int n)->int{ int s=0; for(int k=1;k<=n;k++){ s+=k; } return s; }; cout << "La somme de 1 a "<< 7<<"="<<somme(7)<<endl; // l'identifiant du type de retour peuvent être omis puisque le type est défini par la valeur retournée auto fact=[](int n){ int f=1; for(int k=1;k<=n;k++){ f*=k; } return f; }; cout << "La factorielle de 7 = "<<fact(7); return 0; }
Résultat
La somme de 1 a 7=28 La factorielle de 7 = 5040
Les fonctions lambda peuvent capturer des variables externes et les utiliser lors du calcul du résultat. Pour ce faire, nous listons les variables entre crochets lors de la description de la fonction lambda. Par exemple, le code suivant détermine la fonction lambda evaluer() avec un argument x de type double et capture deux variables externes a et b :
Exemple 3 : Un programme complet dans lequel nous utilisons des fonctions lambda.
#include <iostream> using namespace std; int main() { double a=2.5,b=3; auto evaluer=[a,&b](double x)->double{ return a*x+b; }; cout << "evaluer(2)= "<<evaluer(2)<<endl; cout << "evaluer(4.5)= "<<evaluer(4.5); return 0; }
Résultat
evaluer(2)= 8 evaluer(4.5)= 14.25
Les variables a et b, ainsi que le paramètre x, sont utilisées lors du calcul du résultat de la fonction. Ces variables doivent être initialisées avant la définition de la fonction lambda. Un autre point important est que la variable a est capturée par valeur et la variable b est capturée par référence (nous avons utilisé l'instruction & avant le nom de la variable). Si une variable est capturée par valeur, la fonction lambda utilisera la valeur de la variable qu'elle avait lors de la création de la fonction lambda. Si une variable est capturée par référence, la fonction lambda utilise la valeur de la variable qu'elle possède lorsque la fonction est appelée.
Exemple 4
#include <iostream> using namespace std; int main() { double a=2.5,b=3; auto evaluer=[a,&b](double x)->double{ return a*x+b; }; cout <<"evaluer(2)= "<evaluer(2)<<endl; a=3; b= 2.5; cout <"evaluer(2)= "<evaluer(2); return 0; }
Résultat
evaluer(2)= 8 evaluer(2)= 7.5
Dans le programme ci-dessus, nous définissons les variables a et b avec les valeurs 2,5 et 3, respectivement. Après cela, nous créons la fonction lambda evaluer(), qui pour le paramètre x renvoie la valeur de a*x+b. Ainsi, pour la valeur 2 du paramètre, nous obtenons 8 comme résultat de la fonction (2,5*2+3). Ensuite, nous utilisons les instructions a=3 et b=2.5 pour affecter de nouvelles valeurs aux variables a et b. L'appel de la fonction evaluer() avec le même paramètre donne la valeur 7,5. Pourquoi en est-il ainsi ? Étant donné que la variable a est capturée par valeur, l'affectation d'une nouvelle valeur à la variable n'affecte pas la fonction lambda. Il utilise toujours l'ancienne valeur 2.5 pour la variable a. Contrairement à cela, la variable b est capturée par référence. Ainsi, après avoir attribué la valeur 2.5 à la variable, la fonction lambda utilise cette nouvelle valeur. Par conséquent, la fonction lambda renvoie la valeur de l'expression 2*2,5+2,5 qui est 7,5.
Si nous voulons capturer (par valeur) toutes les variables externes, nous pouvons mettre l'instruction = entre crochets dans la description de la fonction lambda. L'instruction & entre crochets signifie que toutes les variables externes sont capturées par référence.
Exemple 5
#include <iostream> using namespace std; int main() { double a=2.5,b=3; auto evaluer=[&](double x)->double{ return a*x+b; }; cout <<"evaluer(2)= "<<evaluer(2)<<endl; a=3; b= 2.5; cout <<"evaluer(2)= "<<evaluer(2); return 0; }
Résultat
evaluer(2)= 8 evaluer(2)= 8.5