M2103-TP8-Exo-2

Il existe une fonction de recherche générique standard très
puissante :

template <class InputIterator, class Predicate>
InputIterator find_if (InputIterator first, InputIterator last,
                       Predicate pred);

Comme son nom l’indique, elle recherche, entre les itérateurs first et last d’un conteneur, la position du premier élément qui satisfait une condition, représentée par le functor pred qui doit être un prédicat.

Cela signifie que la surcharge de l’opérateur () a le
profil suivant :

    bool operator () const (const T &);

Le principe en est le même que précédemment.

Travail à effectuer

Créer le projet FunctorFind.

Copier dans le fichier main.cpp le fichier de test FunctorSort.cpp FunctorSort.cpp, qui est le corrigé de l’exercice FunctorSort Functors et algorithmes de tri” ci-dessus.

/**
 *
 * @file     FunctorSort.cpp
 *
 * @authors  M. Laporte, D. Mathieu
 *
 * @date     07/12/2011
 *
 * @version  V1.0
 *
 **/
#include <string>
#include <vector>
#include <algorithm> // sort()
#include <iostream>

using namespace std;

namespace
{
    template <typename T>
    class ILessThanGen
    {
      public :
        virtual ~ILessThanGen (void) {}
        virtual bool operator () (const T &, const T &) const = 0;

    }; // ILessThanGen

    class Pers
    {
        string   myNom;
        unsigned myAge;

      public :
        Pers (const string & Nom, unsigned Age)
            : myNom (Nom), myAge (Age) {}

        const string & getNom (void) const noexcept { return myNom; }
        unsigned       getAge (void) const noexcept { return myAge; }

    private :
        ostream & display (ostream & os)  const
        {
            return os << getAge () << " - " << getNom ();

        } // display()

      public :
        friend ostream & operator << (ostream & os, const Pers & p)
        {
            return p.display (os);

        }

    }; // Pers

    class TriParAgeAsc : public ILessThanGen <Pers>
    {
      public :
        virtual ~TriParAgeAsc (void) noexcept {}

        virtual bool operator () (const Pers & p1, const Pers & p2)
                        const noexcept
        {
            return p1.getAge () < p2.getAge ();

        } // operator ()

    }; // TriParAgeAsc

    class TriParNomDesc : public ILessThanGen <Pers>
    {
      public :
        virtual ~TriParNomDesc (void) noexcept {}

        virtual bool operator () (const Pers & p1, const Pers & p2)
                        const noexcept
        {
            return p1.getNom () > p2.getNom ();

        } // operator ()

    }; // TriParNomDesc

    void functorSort (void)
    {
        cout << "FunctorSort : \n";

        typedef vector <Pers> CVPers;

        CVPers vPers;

        vPers.push_back ( Pers ("Charlotte", 21));
        vPers.push_back ( Pers ("Alfred",    12));
        vPers.push_back ( Pers ("Jean",      42));
        vPers.push_back ( Pers ("Noemie",    11));
        vPers.push_back ( Pers ("Berthe",    99));
        vPers.push_back ( Pers ("Agathe",    29));
        vPers.push_back ( Pers ("Sylvain",   42));
        vPers.push_back ( Pers ("Pierre",    75));

        cout << "\nTri par age croissant\n\n";

        sort (vPers.begin (), vPers.end (), TriParAgeAsc ());

        for (const Pers & personne : vPers)
            cout << personne << '\n';

        cout << "\nTri par nom decroissant\n\n";

        sort (vPers.begin (), vPers.end (), TriParNomDesc ());

        for (const Pers & personne : vPers)
            cout << personne << '\n';

    } // functorSort()

} // namespace

int main (void)
{
    functorSort ();

    return 0;

} // main()

Remplacez la classe ILessThanGen par IPredicatGen et supprimez tout ce qui concerne les tris.

Dérivez publiquement cette classe en SelParTrancheAge, qui possède :

  • les deux données-membres myAgeMin et myAgeMax,
  • un constructeur qui permet de les initialiser,
  • un destructeur virtuel,
  • la surcharge de l’opérateur (), qui renvoie true lorsque l’âge de l’élément qui lui est passé en paramètre est dans l’intervalle [myAgeMin, myAgeMax].

Testez en utilisant et en complétant la fonction suivante :

    void functorFind (void)
    {
        cout << "FunctorFind : \n";

        typedef vector <Pers> CVPers;
        CVPers vPers;

        vPers.push_back ( Pers ("Charlotte", 21));
        vPers.push_back ( Pers ("Alfred",    12));
        vPers.push_back ( Pers ("Jean",      42));
        vPers.push_back ( Pers ("Noemie",    11));
        vPers.push_back ( Pers ("Berthe",    99));
        vPers.push_back ( Pers ("Agathe",    29));
        vPers.push_back ( Pers ("Sylvain",   42));
        vPers.push_back ( Pers ("Pierre",    75));

        for (const Pers & personne : vPers)
            cout << personne << '\n';

        CVPers::const_iterator pos;

        cout << "\nRecherche sur  43 <= age <= 75 : ";

        pos = find_if (vPers.begin (), vPers.end (), // a completer
        if (vPers.end () ==pos)
            cout << "Aucun element ne correspond a ce critere\n";
        else
            cout << *pos << '\n';

        cout << "\nRecherche sur  43 <= age <= 45 : ";

        pos = find_if (vPers.begin (), vPers.end (), // a completer
        if (vPers.end () == pos)
            cout << "Aucun element ne correspond a ce critere\n";
        else
            cout << *pos << '\n';

        cout << '\n';

    } // functorFind()

Lorsque vous obtenez des résultats conformes à l’affichage attendu ci-dessous,

FunctorFind : 
21 - Charlotte
12 - Alfred
42 - Jean
11 - Noemie
99 - Berthe
29 - Agathe
42 - Sylvain
75 - Pierre

Recherche sur  43 <= age <= 75 : 75 - Pierre

Recherche sur  43 <= age <= 45 : Aucun element ne correspond a ce critere
  • dérivez publiquement la classe IPredicatGen en SelParNomMin, qui possède :
    • la donnée-membre myNomMin,
    • un constructeur qui permet de l’initialiser,
    • un destructeur virtuel,
    • la surcharge de l’opérateur (), qui renvoie true lorsque le nom de l’élément qui lui est passé
      en paramètre est supérieur à celui de myNomMin.
  • ajoutez à la fonction functorFind() la séquence suivante :
            cout << "\nRecherche sur nom > Noemie : ";
    
            pos = find_if (vPers.begin (),  vPers.end (), // a completer
    
            if (vPers.end () == pos)
                cout  << "Aucun element ne correspond a ce critere\n";
            else
                cout << *pos << '\n';
    
            cout  << "\nRecherche sur nom > alfred : ";
    
            pos = find_if (vPers.begin (),  vPers.end (), // a completer
    
            if (vPers.end () == pos)
                cout  << "Aucun element ne correspond a ce critere\n";
            else
                cout << *pos << '\n';
    

Vous devez obtenir des résultats conformes à la deuxième partie de l’affichage attendu ci-dessous.

Lorsque les affichages sont corrects, une dernière modification est proposée : on remarque que la classe IPredicatGen manque encore de généralité, car le résultat est toujours un booléen (c’est pour cela que c’est un prédicat).

En réalité, dans certaines applications, on peut supposer avoir besoin de functors à un seul paramètre qui renvoient des types différents du type booléen.

Il suffit d’ajouter en second paramètre de généricité le type de retour : cela s’appelle une fonction-objet unaire ou unary function.

Ajoutez la classe IUnaryFunction, copie de IPredicatGen, à laquelle est ajouté le second paramètre de généricité TRes représentant le type de retour de la fonction.

Modifiez les deux classes SelParTrancheAge et SelParNomMin, en en faisant des UnaryFunction‘s et tester à nouveau.

résultats supplémentaires attendus :

Recherche sur nom > Noemie : 42 - Sylvain

Recherche sur nom > alfred : Aucun element ne correspond a ce critere