Appliquer un prédicat ou une fonction aux éléments d'une séquence en C++ - Bibliothèque STL

06 Mar 2022 06 Mar 2022 1776 vues ESSADDOUKI Mostafa 11 min de lecture
Introduction et syntaxe de base
1 Introduction au langage C++ 2 Entrée-sortie en C++ - cin et cout 3 Inférence de type avec le mot-clé auto en C++ 4 Classe std::string et les chaînes de caractères en C++ 5 Les structures conditionnelles (if et switch) en C++ (C++17 et C++20) 6 Les boucles en C++ (C++17 et C++20) 7 La gestion des fichiers en C++
Pointeurs et fonctions
8 Introduction aux pointeurs en C++ - Déclaration et interêts 9 Les références en C++ - déclaration et interêts 10 Les tableaux en C++ - Déclaration et interêts 11 Introduction aux fonctions en C++ 12 Passer des arguments à une fonction en C++ 13 Déclarer un paramètre const en C++ 14 Les fonctions Lambda en C++ 15 Fonctions utiles (Mathématiques et caractères) en C++
Programmation OO
16 Classes et objets en C++ 17 Spécificateurs d'accès en C++ 18 Constructeurs et destructeur d'une classe en C++ 19 Fonctions membres en C++ 20 Membres statiques d'une classe en C++ 21 Fonctions en ligne en C++ - inline 22 Fonctions et classes amies en C++ - friend 23 Surcharge des fonctions en C++ 24 Surcharge des opérateurs en C++ 25 Héritage en C++ 26 La gestion d'exceptions en C++ : déclaration, utilisation et personnalisation 27 fonctions et classes templates en C++ 28 Les nouveautés C++20 pour améliorer les templates en C++
Structures de données
29 Introduction aux structures de données 30 Les structures en C++ et la différence avec les structures en C 31 Les listes chaînées en C++ 32 Les piles en C++ 33 File d'attente en C++ 34 Arbre binaire de recherche : définition et mise en oeuvre en C++
La bibliothèque standard (STL)
35 Introduction à la bibliothèque de Template Standard STL 36 Les itérateurs en C++ - définition, déclaration et exemples 37 La classe array en C++ (bibliothèque STL) <array> 38 La classe vector de la bibliothèque STL <vector> 39 La classe deque en C++ ( Bibliothèque STL) 40 La classe list en C++ (bibliothèque STL) <list> 41 La classe stack (Pile) en C++ (bibliothèque STL) <stack> 42 La classe queue (File d'attente) en C++ (bibliothèque STL) <queue> 43 La file d'attente prioritaire (classe priority_queue) - Bibliothèque STL 44 Les ensembles en C++ (Classe set <set> - Bibliothèque STL) 45 Les dictionnaires en C++ : Classe map (Bibliothèque STL) 46 Introduction aux algorithmes de la bibliothèque STL (programmation compétitive) 47 Tri et méthodes associées en C++ - Bibliothèque STL 48 Recherche dichotomique et méthodes associées en C++ - Bibliothèque STL 49 Appliquer un prédicat ou une fonction aux éléments d'une séquence en C++ - Bibliothèque STL 50 Recherche dans une séquence et méthodes associées en C++ - Bibliothèque STL

Les séquences sont très utiles pour stocker des éléments, mais parfois nous avons besoin d'un moyen de vérifier si tous ou certains éléments vérifient un prédicat, ou d'appliquer une fonction sur chaque élément. Dans ce cours, nous découvrirons des fonctions déjà définies dans la bibliothèque STL pour résoudre ces problèmes.

Définition — Algorithmes STL La bibliothèque STL (Standard Template Library) propose des algorithmes génériques travaillant sur des séquences (tableaux, vecteurs, deques…) via des itérateurs. Les algorithmes présentés ici appartiennent à la catégorie des opérations de séquence non-modificatrices définies dans <algorithm>.
Mot « algorithme » dans ce cours Nous utilisons le mot algorithme pour faire référence à ce que fait la fonction (son comportement), indépendamment de son implémentation interne.
   
En-tête requis C++
#include <algorithm>   // all_of, any_of, none_of, for_each, for_each_n
#include <execution>   // politiques d'exécution (C++17)
using namespace std;
Vue d'ensemble — Les 5 algorithmes
FonctionRetourne vrai si…Séquence vide →
all_oftous les éléments vérifient predtrue
any_ofau moins un élément vérifie predfalse
none_ofaucun élément ne vérifie predtrue
for_eachApplique fct à chaque élément → retourne fct
for_each_nApplique fct aux n premiers éléments → retourne it_begin+n

Fonction all_of

L'algorithme all_of détermine si chaque élément d'une séquence répond à certains critères spécifiés par l'utilisateur.

Il renvoie true si la séquence cible est vide ou si pred est true pour tous les éléments ; sinon il renvoie false.

   
Syntaxe — all_of C++
bool all_of([ep], it_debut, it_fin, pred);

Arguments :

  • Une politique d'exécution std::execution facultative, ep (défaut : std::execution::seq)
  • Une paire d'objets InputIterator, it_debut et it_fin, représentant la séquence cible
  • Un prédicat unaire, pred, qui accepte un élément de la séquence cible
Définition — Prédicat Un prédicat est une fonction (ou expression lambda) qui prend un élément en paramètre et retourne un bool. Exemple : bool positif(int a) { return a > 0; }

  Exemple 1 — Vérifier si tous les éléments sont positifs

#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;

bool positif(int a) { return a > 0; }

int main() {
    int vals1[] = {1, 2, 3, 2, 3, 4, -5, 13, 8, 9, 6};  // contient -5
    int vals2[] = {1, 2, 3, 2, 3, 4,  5, 13, 8, 9, 6};  // tous positifs
    deque<int> d1(vals1, vals1 + 11);
    deque<int> d2(vals2, vals2 + 11);

    // Vérifier si tous les éléments de d1 sont positifs
    if (all_of(d1.begin(), d1.end(), positif))
        cout << "tous les éléments de d1 sont positifs\n";
    else
        cout << "Il y a au moins un élément négatif ou nul dans d1\n";

    if (all_of(d2.begin(), d2.end(), positif))
        cout << "tous les éléments de d2 sont positifs\n";
    else
        cout << "Il y a au moins un élément négatif ou nul dans d2\n";

    return 0;
}
Résultat
Il y a au moins un élément négatif ou nul dans d1
tous les éléments de d2 sont positifs
Équivalent avec lambda C++11 On peut remplacer la fonction nommée positif par une expression lambda:
all_of(d1.begin(), d1.end(), [](int a){ return a > 0; });
Les lambdas évitent de déclarer une fonction séparée pour des prédicats simples.

Fonction any_of

L'algorithme any_of détermine si au moins un élément d'une séquence répond à certains critères spécifiés par l'utilisateur.

Il renvoie false si la séquence cible est vide ou si pred n'est true pour aucun élément ; sinon il renvoie true.

   
Syntaxe — any_of C++
bool any_of([ep], it_debut, it_fin, pred);

Arguments :

  • Une politique d'exécution std::execution facultative, ep
  • Une paire d'objets InputIterator, it_debut et it_fin
  • Un prédicat unaire, pred

  Exemple 2 — Vérifier si la séquence contient au moins un nombre pair

#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;

bool estpair(int a) { return a % 2 == 0; }

int main() {
    int vals1[] = {1, 2, 3, 2, 3, 4, 5, 13, 8, 9, 6};   // contient des pairs
    int vals2[] = {1, 5, 3, 9, 3, 13, 5, 13, 21, 9, 35}; // tous impairs
    deque<int> d1(vals1, vals1 + 11);
    deque<int> d2(vals2, vals2 + 11);

    if (any_of(d1.begin(), d1.end(), estpair))
        cout << "La séquence d1 contient un nombre pair\n";
    else
        cout << "La séquence d1 ne contient pas un nombre pair\n";

    if (any_of(d2.begin(), d2.end(), estpair))
        cout << "La séquence d2 contient un nombre pair\n";
    else
        cout << "La séquence d2 ne contient pas un nombre pair\n";

    return 0;
}
Résultat
La séquence d1 contient un nombre pair
La séquence d2 ne contient pas un nombre pair

Fonction none_of

L'algorithme none_of détermine si aucun élément d'une séquence ne répond à certains critères spécifiés par l'utilisateur.

Il renvoie true si la séquence cible est vide ou si pred est true pour aucun élément ; sinon il renvoie false.

   
Syntaxe — none_of C++
bool none_of([ep], it_debut, it_fin, pred);

Arguments :

  • Une politique d'exécution std::execution facultative, ep
  • Une paire d'objets InputIterator, it_debut et it_fin
  • Un prédicat unaire, pred
Relation entre all_of, any_of et none_of Pour une séquence non vide et un prédicat pred:
  • none_of(pred) est équivalent à !any_of(pred)
  • all_of(pred) implique any_of(pred) (si la séquence est non vide)
  • Si all_of(pred) est vrai, alors none_of(!pred) est aussi vrai

  Exemple 3 — Vérifier qu'aucun élément n'est pair

#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;

bool estpair(int a) { return a % 2 == 0; }

int main() {
    int vals1[] = {1, 2, 3, 2, 3, 4, 5, 13, 8, 9, 6};   // contient des pairs
    int vals2[] = {1, 5, 3, 9, 3, 13, 5, 13, 21, 9, 35}; // tous impairs
    deque<int> d1(vals1, vals1 + 11);
    deque<int> d2(vals2, vals2 + 11);

    // none_of retourne true si AUCUN élément ne vérifie pred
    if (none_of(d1.begin(), d1.end(), estpair))
        cout << "La séquence d1 ne contient pas un nombre pair\n";
    else
        cout << "La séquence d1 contient un nombre pair\n";

    if (none_of(d2.begin(), d2.end(), estpair))
        cout << "La séquence d2 ne contient pas un nombre pair\n";
    else
        cout << "La séquence d2 contient un nombre pair\n";

    return 0;
}
Résultat
La séquence d1 contient un nombre pair
La séquence d2 ne contient pas un nombre pair
Remarque sur les résultats d'any_of et none_of Les exemples 2 et 3 produisent la même sortie avec les mêmes séquences. C'est attendu : none_of(pred) = !any_of(pred). La différence réside dans l'intention sémantique exprimée dans le code.

Fonction for_each

L'algorithme for_each applique une fonction définie par l'utilisateur à chaque élément d'une séquence.

Bien que for_each soit considéré comme une opération non-modificatrice, si it_debut est un itérateur mutable, fct peut modifier les éléments. Toutes les valeurs que fct renvoie sont ignorées.

Si on omet ep, for_each retourne fct ; sinon, il renvoie void.

   
Syntaxe — for_each C++
for_each([ep], it_debut, it_fin, fct);

Arguments :

  • Une politique d'exécution std::execution facultative, ep
  • Une paire d'objets InputIterator, it_debut et it_fin
  • Une fonction unaire, fct, qui accepte un élément de la séquence

  Exemple 4 — Afficher et calculer les carrés de chaque élément

#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;

void afficher(int a)   { cout << a << " "; }
void puissance(int p)  { cout << p * p << " "; }

int main() {
    int vals[] = {1, 2, 3, 2, 3, 4, 5, 13, 8, 9, 6};
    deque<int> v(vals, vals + 11);

    // Afficher tous les éléments
    for_each(v.begin(), v.end(), afficher);
    cout << '\n';

    // Afficher le carré de chaque élément
    for_each(v.begin(), v.end(), puissance);
    cout << '\n';

    return 0;
}
Résultat
1 2 3 2 3 4 5 13 8 9 6
1 4 9 4 9 16 25 169 64 81 36
for_each vs boucle for classique for_each est souvent remplaçable par une boucle for classique ou un range-based for (C++11) :
for (int x : v) cout << x << " ";           // range-based for
for_each(v.begin(), v.end(), afficher);      // for_each — équivalent
L'avantage de for_each est sa compatibilité avec les politiques d'exécution parallèle (std::execution::par).

Fonction for_each_n

L'algorithme for_each_n applique une fonction définie par l'utilisateur aux n premiers éléments d'une séquence.

Il applique fct à chaque élément dans l'intervalle [it_begin, it_begin + n). Les valeurs retournées par fct sont ignorées. La fonction retourne it_begin + n.

   
Syntaxe — for_each_n C++
InputIterator for_each_n([ep], it_begin, n, fct);

Arguments :

  • Une politique d'exécution std::execution facultative, ep
  • Un InputIterator it_begin — premier élément de la séquence cible
  • Un entier n — nombre d'éléments à traiter ; l'intervalle est [it_begin, it_begin + n)
  • Une fonction unaire, fct
Différence clé — for_each vs for_each_n
Aspectfor_eachfor_each_n
PortéeToute la séquence [début, fin)Les n premiers éléments [début, début+n)
Borne finaleit_fin requisn requis, it_fin non nécessaire
Valeur retournéefct (sans ep)it_begin + n
Disponible depuisC++98C++17

  Exemple 5 — Appliquer une fonction aux 4 premiers éléments seulement

#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;

void afficher(int a)  { cout << a << " "; }
void puissance(int p) { cout << p * p << " "; }

int main() {
    int vals[] = {1, 2, 3, 2, 3, 4, 5, 13, 8, 9, 6};
    deque<int> v(vals, vals + 11);

    // Afficher les 4 premiers éléments
    for_each_n(v.begin(), 4, afficher);
    cout << '\n';

    // Carré des 4 premiers éléments
    for_each_n(v.begin(), 4, puissance);
    cout << '\n';

    return 0;
}
Résultat
1 2 3 2
1 4 9 4
Utilisation de la valeur retournée for_each_n retourne un itérateur pointant sur l'élément juste après le dernier traité. Cela permet de chaîner les opérationsou de reprendre le traitement là où on s'est arrêté :
auto it = for_each_n(v.begin(), 4, afficher);  // traite v[0]..v[3]
for_each_n(it, 3, afficher);                   // traite v[4]..v[6]

Récapitulatif — Algorithmes de séquence non-modificateurs

FonctionSignature simplifiéeRôleRetourne
all_ofall_of(deb, fin, pred)Tous les éléments vérifient pred ?bool
any_ofany_of(deb, fin, pred)Au moins un élément vérifie pred ?bool
none_ofnone_of(deb, fin, pred)Aucun élément ne vérifie pred ?bool
for_eachfor_each(deb, fin, fct)Applique fct à chaque élémentfct
for_each_nfor_each_n(deb, n, fct)Applique fct aux n premiers élémentsdeb + n

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.