STL - UQAC

Transcription

STL - UQAC
Les conteneurs associatifs
La bibliothèque standard contient plusieurs conteneurs permettant l’ajout,
la suppression et la recherche dans des équences ordonnées d’objets. Ces
classes sont généralement implémentées à l’aide d’arbres binaires de recherche
auto-balancés. La plupart des implémentation utilisent des arbres rouge-noirs
plutôt que des arbres AVL.
1
La classe map
Nous commencerons par discuter de la classe map. Un map (aussi appelé
dictionnaire) est un conteneur gérant des paires de valeurs. Il permet, à
partir d’une valeur (la clef), d’accéder à une autre (la valeur mappée). La
définition d’un map ressemble à:
template<class Clef, class Valeur>
class map
{
public:
typedef Clef key_type;
typedef Valeur mapped_type;
typedef pair<const Clef,Valeur> value_type;
iterator begin();
iterator end();
mapped_type& operator[] (const key_type& k);
iterator find(const key_type& k);
pair<iterator, bool> insert(const value_type& val);
void erase(iterator pos);
int erase(const key_type& k);
void clear();
// autres membres
};
Les seules conditions imposées par le modèle est que l’operateur < doit être
défini pour les éléments de type key_type et un constructeur de défaut doit
1
être défini pour les éléments de type mapped_type. Le type value_type est
défini à l’aide de la structure pair déclarée dans le fichier d’en-tête <utility>:
template<class A, class B>
struct pair
{
A first;
B second;
pair(const A& x, const B& y) : first(x), second(y) {}
};
Les itérateurs de la classe map pointent vers les noeuds de l’arbre contenant des éléments de type value_type. Ils servent à parcourir une séquence
de noeuds selon l’ordre défini pour key_type. Par exemple, supposons que
M est un conteneur de type map<string, int> alors il est possible d’afficher
la séquence ordonnée de ses éléments à l’aide du code suivant:
map<string,int>::iterator it;
for (it=M.begin(); it!=M.end(); it++)
cout<< it->first << "\t" << it->second <<endl;
La principale caractéristique de la classe map est l’examen associatif des
éléments fourni par l’opérateur d’indiçage (operator[]). Contrairement au
vector, l’indice est de type const key_type n’est pas nécessairement un
entier. Poursuivant avec l’exemple précédent, on pourrait écrire:
string s;
cin >> s;
cout << M[s];
L’opération d’indiçage M [s] identifie la paire (string, int) appropriée et retourne une référence de sa partie int. Si la clef s n’est pas trouvée, la paire
(s, d) sera crée et ajoutée à M , où d est la valeur de défaut d’un mapped_type.
C’est la raison pour laquelle la classe mapped_type doit posséder un constructeur par défaut. Une instruction telle que
M["INF"]=109;
2
aura donc pour effet, ou bien de modifier une valeur existante, ou encore
d’ajouter la nouvelle paire (00 IN F 00 , 109).
La fonction insert tente d’ajoutee une paire (s, k) au conteneur M . Les
clefs d’un map étant unique, l’insertion est réalisée seulement s’il n’existe
pas déjà un élément dans M possédant la clef s. La valeur retournée est une
pair<iterator, bool>. Le bool est 1 si l’insertion a réussi et 0 sinon. Dans
les deux cas, après l’insertion M contiendra un élément dont la clef est s et
l’iterateur retourné pointera vers cet élément.
2
La classe set
La classe set peut être vu comme un cas particulier de la classe map où les
deux composantes d’une paire sont identiques. On utilise un set lorsque la
clef est la seule information qui nous intéresse.
template<class Clef>
class set
{
public:
typedef Clef value_type;
typedef Clef key_type;
iterator begin();
iterator end();
iterator find(const key_type_type& k);
pair<iterator, bool> insert(const value_type& val);
void erase(iterator pos);
int erase(const key_type& k);
void clear();
// autres membres
};
On remarque que la définition des classe set et map sont similaires. La
principale différence est l’absence de l’opérateur d’indiçage dans la classe set.
3
3
Multimap et multiset
Les multimap et les multiset sont similaires aux classes map et set sauf que
plusieurs éléments peuvent posséder la même clef. Les fonctions membres
suivantes sont aussi disponibles pour les classes set et map mais sont surtout
utiles pour les multiset et les multimap.
iterator lower_bound(const key_type& k);
iterator upper_bound(const key_type& k);
pair<iterator,iterator> equal_range(const key_type& k);
La fonction lower(k) retourne un itérateur pointant sur le premier éléments
possédant la clef k. La fonction upper(k) retourne un itérateur vers le premier élément dont la clef est supérieure à k. Dans les deux cas, si l’élément
recherché est inexistant, l’iterateur end() est retourné. Finalement, la fonction equal_range(const clef& k) retourne la paire d’itérateurs (lower(k), upper(k)). L’exemple suivant illustre comment afficher de façon ordonnée tous
les éléments de m possédant la clef s.
void afficher(multimap<string,int>& m, string s)
{
typedef multimap<string,int>::iterator MI;
pair<MI,MI> g=m.equal_range(s);
for(MI p=g.first; p!=g.second; ++p)
cout<<p->Elem<<endl;
}
4
Utilisation des objets fonctions
Les modèles set, map, multi_set et multi_map utilisent un paramètre supplémentaire qui n’a pas été mentionné dans les sections précédentes. Par
exemple la classe map ressemble à:
4
template<class Clef, class Valeur, class Cmp=less<Clef> >
class map
{
public:
typedef Clef key_type;
typedef Valeur mapped_type;
typedef pair<const Clef,Valeur> value_type;
typedef Cmp key_compare;
// Les reste est identique sauf que les occurences x<y
// sont remplacées par key_compare(x,y)
};
Le paramètre Cmp est utilisé comme fonction de comparaison des clefs.
Lorsque Cmp n’est pas spécifié, la valeur par défaut est l’objet fonction less
défini dans le fichier d’en-tête <functional>:
template <class Elem>
struct less
{
bool operator()(const Elem& a, const Elem& b)
{
return (a<b);
}
};
Ainsi, lorsque l’on ne spécifie pas le paramètre Cmp, il est nécessaire
que l’opérateur de comparaison < soit définit pour les objets de type Clef.
Dans certaine situation, il est préférable de fournir explicitement le paramètre
Cmp. Considérons par exemple un dictionnaire map<string, string> dans
lequel les mots sont ordonnés sans tenir compte des lettres minuscules et
majuscules. L’opérateur par défaut des chaı̂nes de caractères ne peut pas
être utilisé puique, avec le jeu de caractère ASCII, on aura "Zoo" < "ane".
Nous devons donc construire notre propre opérateur de comparaison sous la
forme d’un objet fonction:
5
struct SansMajuscule
{
bool operator()(const string&, const string&) const;
};
/*
* retourne x<y sans tenir compte des majuscules.
*/
bool SansMajuscule::operator()(const string& x, const string& y) const
{
string:: const_iterator p=x.begin();
string:: const_iterator q=y.begin();
// toupper est défini dans <ctype.h>
while (p!=x.end() && q!=y.end() && toupper(*p)==toupper(*q))
{
++p;
++q;
}
if (p==x.end()) return q!=y.end();
if (q==y.end()) return false;
return toupper(*p)<toupper(*q);
}
L’exemple suivant montre comment créer et utiliser un dictionnaire possédant
la propriété désirée:
map<string, string, SansMajuscule> Dictionnaire;
D["professeur"]="Titre d’un enseignant au debut du trimestre";
D["oppresseur"]="Titre d’un enseignant a la fin du trimestre";
D["Zombi"]="Etat d’un etudiant a la fin du trimestre";
D["Voisins du sud"]="Nom que l’on donne aux americains";
D["CRETIN du sud"]="Nom que l’on donne au président americain";
map<string,string,SansMajuscule>::iterator it;
for (it=D.begin(); it!=D.end(); ++it)
cout<<it->first<<"\t"<<it->second<<endl;
6