class - DCanL
Transcription
class - DCanL
Département Informatique Programmation objet Cours n°6 Structures de données – partie 2 Conteneurs évolués C++ algorithmes génériques 1 Département Informatique Le conteneur std::set<> Souvent nous avons besoin d'une structure de donnée représentant un ensemble d'éléments, non ordonnés. Ni une liste, ni un tableau, ne représentent correctement ces notions. Leur structure fait que la recherche par valeur est pénalisante. La classe std::set<>représente un ensemble de données non ordonnées, pour laquelle la recherche par la valeur est optimisée. La valeur n'est présente qu'une seule fois dans le conteneur. 2 Département Informatique Opérations standard sur un std::set<T> • insert(T) • erase(i), i étant un std::set<T>::iterator • find(T) • begin(), end(), etc... comme toute collection C++ • Empty() • count(T) • etc... std::set<>contient une classe iterator qui permet de parcourir le conteneur. L'ordre utilisé est celui fourni par l'opérateur < (sauf si un autre opérateur de comparaison est fourni, voir predicats) 3 Département Informatique Exemple d'utilisation d'un std::set<> std::set< char > Voyelles; Voyelles.insert('A'); Voyelles.insert('E'); Voyelles.insert('O'); Voyelles.insert('I'); Voyelles.insert('U'); std::cout<< Voyelles.size() << " voyelles."; char c; do { std::cin>>c; if(Voyelles.count(c)>0) std::cout<<c<<" est une voyelle"; } while(c!='\n'); 4 Département Informatique Conteneur std::multiset<> Ce conteneur est très proche de std::set<>, mais il supporte plusieurs occurrences de la même valeur. Son fonctionnement est très proche, il possède les mêmes opérations. 5 Département Informatique Conteneur associatif std::map<> Quand les données à stocker sont organisées à l'aide d'une clef, les conteneurs usuels (tableau, liste, etc...) ne sont pas adaptés. L'utilisation du conteneur std::map<>permet d'avoir un stockage des éléments en tenant compte d'une clé qui n'est pas forcément la position (comme dans vector) template <class key, class value> class map {}; std::map<>garantit une recherche rapide dans la « table » lorsqu'on fournit la clé. std::map<int, T> ressemble à std::vector<T> 6 Département Informatique Opérations standard • operator[] (key)pour lire/écrire/insérer des valeurs • find(T) • count(T) • empty(), size() • clear(), erase() • begin(), end() std::map<>contient une classe iterator pour parcourir la collection. L'itérateur référence un objet std::pair<T1,T2>défini comme : template <class T1, class T2> struct pair { T1 first; T2 second; }; 7 Département Informatique Exemple d'utilisation de std::map<> using namespace std; map< string, string> Repertoire; Repertoire["guidet"] = " [email protected]"; Repertoire["nicolle"] = " [email protected]"; for(map<string,string>::iterator i=Repertoire.begin( i!=Repertoire.end(); ++i) { cout<<"Nom :"<<i->first; cout<<"\tMail :"<<i->second; } if(Repertoire.count("cruz")==0) cout<<"Mail de christophe cruz introuvable !"; 8 Département Informatique Le conteneur std::multimap<> Permet d'avoir plusieurs valeurs pour chaque « clé » Les opérations sont identiques à la map, sauf l'opérateur [] qui n'est pas défini. 9 Département Informatique Utilisation avancée des conteneurs standard Une utilisation fréquente des modèles C++ est le développement de conteneurs. Mais un algorithme peut éalement profiter du polymorphisme de généricité, si un traitement est générique et doit être passé en paramètre. Un tel type (traitement générique) est appelé objet foncteur. 10 Département Informatique Exemple de prédicat Un prédicat est une fonction booléenne, représentant une condition à satisfaire. Imaginons par exemple que l'on veuille afficher tous les éléments d'un tableau (type std::vector) dont les éléments sont inférieurs à un élément donné. Le code ressemblerait à : for(int i=0; i<Tableau.size(); i++) { Et si on veut changer if( Tableau[i] < ValeurDonnee) la condition, il faut std::cout<<Tableau[i]; changer le code !!! } 11 Département Informatique Solution : utiliser un prédicat. template <class predicat> void AfficheSi(const std::vector<int>& V, predicat f) { for(int i=0;i<V.size();i++) if( f(V[i]) ) Ceci peut être une fonction, std::cout<<V[i]; ou un objet foncteur } Le code est plus générique, mais limité à un tableau de int... 12 Département Informatique template <class predicat, class elt> void AfficheSi(const std::vector<elt>& V, predicat f { for(int i=0;i<V.size();i++) if( f(V[i]) ) std::cout<<V[i]; } C'est mieux, mais encore limité aux tableaux... Alors qu'on pourrait imaginer utiliser d'autres conteneurs (comme std::list...). Profitons de l'interface commune des conteneurs standards (les iterateurs) pour généraliser un peu plus... 13 Département Informatique template <class predicat, class iterator> void AfficheSi(iterator debut, iterator fin , predicat f) { for(iterator i=debut; i!=fin; ++i) if( f(*i) ) std::cout<< *i; } A présent le code peut s'utiliser pour afficher tous les éléments d'une liste d'entiers différents de -5, tous les éléments d'un tableau de chaînes de moins de trois caractères, etc..., à condition d'écrire le prédicat. 14 Département Informatique Afficher les éléments d'une std::list<std::string> de 3 caractères bool Moins3Car(const std::string& str) { return str.length() == 3; } std::list<std::string> Liste; AfficheSi(Liste.begin(), Liste.end(), Moins3Car ); 15 Département Informatique Afficher les éléments d'un vecteur d'entiers supérieurs à un seuil class SupSeuil { int Seuil; public: SupSeuil(int s):Seuil(s){} bool operator() (int val) const { return val>Seuil; } }; std::vector<int> V; int Seuil; std::cin>>Seuil; AfficheSi(V.begin(), V.end(), SupSeuil(Seuil) ); 16 Département Informatique On pourrait imaginer d'autres utilisation possibles de cette fonction AfficheSi, que l'on peut appeler un algorithme générique. La bibliothèque standard fournit une grande quantité d'algorithmes de ce type, construits de la même manière, avec une très grande généricité. (en-tête : <algorithm> ) Citons entre autres : for_each : Effectue une action non modifiante sur tous les éléments transform : Effectue une action modifiante sur tous les éléments find : effectue une recherche sur tous les éléments... find_if : effectue une recherche en utilisant un prédicat max_element : retrouve le plus grand élément d'une collection sort : trie une collection 17 Département Informatique Il est donc conseillé, si l'un souhaite réaliser un traitement généralisable, d'écrire une algorithme générique. Il est également conseillé, si l'on souhaite effectuer un traitement sur un ensemble, d'utiliser un algorithme standard. La bibliothèque standard fournit également des prédicats, ainsi que d'autres objets foncteurs usuels. (en-tête <functional>) less<> plus<> : encapsule l'opérateur < : encapsule l'opérateur + 18 Département Informatique Exemple : afficher tous les éléments d'une collection... template <class elt> class SortieSurFlux { std::ostream& flux; char Separator; public: SortieSurFlux(std::ostream& f,char sep='\n') :flux(f), Separator(sep){} void operator()(const elt& e) { flux<<e<<Separator; } }; std::list<std::string> L; std::for_each(L.begin(), L.end(), SortieSurFlux(std::cout,'-') ); 19 Département Informatique Exemple : complémenter tous les éléments d'un vector<bool> std::vector<bool> V1, V2; //.... std::transform( V1.begin(), V1.end(), V2.begin(), std::logical_not< bool>() ); Exemple : créer une liste d'entiers résultante de la somme de 2 listes d'entiers std::list<int> L1, L2, L3; //... std::transform( L1.begin(), L1.end(), L2.begin(), L3.begin(), std::plus< int>()); 20 Département Informatique Prochain cours : Structures de données – partie 3 arbres binaires de recherche algorithmes de base A la semaine prochaine ! 21