Outils d'utilisateurs

Outils du Site


comparer_strings

Différences

Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.

Lien vers cette vue

comparer_strings [2014/09/02 00:23]
gbdivers
— (Version actuelle)
Ligne 1: Ligne 1:
  
-^ Chapitre précédent ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ Chapitre suivant ^ 
- 
-====== Tester l'égalité ====== 
- 
-Pour comparer si deux "choses" sont égales, le plus simple sera généralement d'utiliser l'opérateur de comparaison ''=='' que vous avez déjà vu. Cet opérateur fonctionne aussi bien pour les types fondamentaux du C++ (//build-in types//, comme ''int'', ''float'', etc.) que pour certains types de classe (les classes à sémantique de valeur). Il est également possible d'utiliser des algorithmes dans les cas d'utilisation plus spécifiques. 
- 
-===== L'opérateurs de comparaison ===== 
- 
-Dans les chapitres précédents, vous avez vu l'utilisation de l'opérateur de comparaison ''=='' (//egal-to operator//). Les collections de la bibliothèque standard, comme ''vector'', ''array'' ou ''string'', fournissent également cet opérateur. Vous pouvez donc écrire : 
- 
-<code cpp main.cpp> 
-#include <iostream> 
-#include <string> 
- 
-int main() { 
-    std::string const s1 { "salut" }; 
-    std::string const s2 { "salut" }; 
-    std::string const s3 { "hello" }; 
-    std::cout << std::boolalpha; 
-    std::cout << (s1 == s2) << std::endl; 
-    std::cout << (s1 == s3) << std::endl; 
-} 
-</code> 
- 
-affiche : 
- 
-<code> 
-true 
-false 
-</code> 
- 
-Remarque : ne pas oublier les parenthèses autour du test d'égalité pour ''string''. L'opérateur ''<<'' ayant un sens pour cette classe, le compilateur ne pourra pas savoir si vous souhaitez écrire : 
- 
-<code cpp> 
-std::cout << (s1 == s2) << std::endl; 
-std::cout << s1 == (s2 << std::endl); 
-</code> 
- 
-De même pour les collections : 
- 
-<code cpp main.cpp> 
-#include <iostream> 
-#include <vector> 
- 
-int main() { 
-    std::vector<int> const v1 { 1, 2, 3, 4 }; 
-    std::vector<int> const v2 { 1, 2, 3, 4 }; 
-    std::vector<int> const v3 { 4, 3, 2, 1 }; 
-    std::cout << std::boolalpha; 
-    std::cout << (v1 == v2) << std::endl; 
-    std::cout << (v1 == v3) << std::endl; 
-} 
-</code> 
- 
-affiche : 
- 
-<code> 
-true 
-false 
-</code> 
- 
-Avec les collections, l'opérateur d'égalité compare les éléments de la collection un par un et s'il trouve une différence, il retourne ''false''. S'il arrive à la fin de la collection sans trouver de différence, il retourne ''true''. 
- 
-Pour vérifier si deux chaînes sont différentes, il est possible d'utiliser l'opérateur booléen de négation ''!'' : ''!(s1 == s2)''. Plus simplement, on peut directement utiliser l'opérateur de comparaison ''!='' (//not-equal-to operator//). 
- 
-<code cpp main.cpp> 
-#include <iostream> 
-#include <string> 
- 
-int main() { 
-    std::string const s1 { "salut" }; 
-    std::string const s2 { "salut" }; 
-    std::string const s3 { "hello" }; 
-    std::cout << std::boolalpha << (s1 != s2) << std::endl; 
-    std::cout << (s1 != s3) << std::endl; 
-} 
-</code> 
- 
-affiche : 
- 
-<code> 
-false 
-true 
-</code> 
- 
-On voit ici que les opérateurs ''=='' et ''!='' sont étroitement liés. Lorsque l'un de ces deux opérateurs est utilisable, on peut s'attendre à ce que l'autre opérateur le soit également. On dit qu'une classe qui propose l'opérateur d'égalité ''=='' qu'elle respecte le concept de "comparable par égalité" ([[http://en.cppreference.com/w/cpp/concept/EqualityComparable|EqualityComparable]]). Ce concept précise que l'opérateur d'égalité doit suivre les propriétés suivantes : 
- 
-  * réflexivité : quelque soit ''a'', ''a == a'' est toujours vrai ; 
-  * commutativité : si ''a == b'', alors ''b == a'' ; 
-  * transitivité : si ''a == b'' et ''b == c'', alors ''a == c''. 
- 
-Ce concept est assez classique, on le retrouve en mathématique dans la théorie des ensembles. On voit ici un point important : lorsqu'une classe définit un opérateur ''=='', on s'attend à ce qu'elle suive un certain nombre de règles. On dit qu'elle suis une sémantique, cela facilite son utilisation. Du point de vue de l'utilisateur de cette classe, on pourra utiliser n'importe quelle classe respectant cette sémantique de la même façon. Du point de vue du concepteur de la classe (ce que vous apprendrez à faire dans la suite de ce cours), il suffit de définir les sémantiques que l'on souhaite donner à notre classe, l'écriture de la classe en découlera. 
- 
-Au contraire, le non respect d'une sémantique sera très perturbant pour l'utilisateur - et une source d'erreur sans fin. Imaginez que l'opérateur ''=='' ne réalise pas un test d'égalité, mais permet de faire la concaténation de deux chaînes ? 
- 
-Bien sûr, ces considérations s'appliquent à l'ensemble des sémantiques usuelles, en particulier celle que l'on connait en mathématique (addition avec ''+'', soustraction avec ''-'', etc.) 
- 
-===== La sémantique de valeur ===== 
- 
-Un concept complexe peut être décomposé en une série de concepts plus simple (par exemple "est comparable par égalité" est composé des concepts "est réflexif", "est commutatif" et "est transitif" que l'on a vu avant). De la même façon, il est possible de combiner des concepts pour créer de nouveaux concepts plus complexe. Un concept peut également autorisé ou interdire l'utilisation d'autres concepts (par exemple, le concept "est égal" autorise l'utilisation du concept "est différent"). 
- 
-C'est la cas du concept "est comparable par égalité", qui fait parti d'un concept plus général : la sémantique de valeur. Cette sémantique s'applique à tout ce qui représente une valeur : un entier, un nombre réel, un nombre complexe, une chaîne de caractères, un tableau de données, etc. La sémantique de valeur autorise les concepts suivants : 
- 
-**Constructible** par défaut ([[http://en.cppreference.com/w/cpp/concept/DefaultConstructible|DefaultConstructible]]). On peut initialiser avec une valeur par défaut (on parle aussi de "zero initialization" puisque la valeur par défaut sera 0 ou équivalent). 
- 
-<code cpp> 
-int const i {}; // construction par défaut 
-</code> 
- 
-**Copiable** par construction ([[http://en.cppreference.com/w/cpp/concept/CopyConstructible|CopyConstructible]]) et par affectation ([[http://en.cppreference.com/w/cpp/concept/CopyAssignable|CopyAssignable]]). Cela signifie que l'on peut créer une valeur en copiant une autre valeur. Par exemple, pour un entier : 
- 
-<code cpp> 
-int const i { 123 }; 
-int j { i }; // construction par copie 
-j = i;       // affectation par copie 
-</code> 
- 
-**Comparable** par égalité ([[http://en.cppreference.com/w/cpp/concept/DefaultConstructible|EqualityComparable]]) ou par "plus petit que" ([[http://en.cppreference.com/w/cpp/concept/LessThanComparable|LessThanComparable]]). Cela signifie que l'on peut utiliser les opérateurs d'égalité ''=='' et "plut petit que" ''<'' (ainsi que les opérateurs dérivés : "différent de" ''!='', "plus petit ou égal à" <html><=</html>, "plus grand que" ''>'' et "plus grand ou égal à" <html>>=</html>) : 
- 
-<code cpp> 
-int const i { 123 }; 
-int const j { 456 }; 
-cout << (i == j) << endl; // égalité 
-cout << (i != j) << endl; // différent 
-cout << (i < j) << endl;  // plus petit 
-cout << (i <= j) << endl; // plus petit ou égal 
-cout << (i > j) << endl;  // plus grand 
-cout << (i >= j) << endl; // plus grand ou égal 
-</code> 
- 
-Si cela a un sens, la sémantique de valeur permet également de définir des opérateurs arithmétiques classiques : addition ''+'', soustraction ''-'', multiplication ''*'' et division ''/''. Par exemple, pour les entiers : 
- 
-<code cpp> 
-int const i { 123 }; 
-int const j { 456 }; 
-cout << (i + j) << endl; // addition 
-cout << (i - j) << endl; // soustraction 
-cout << (i * j) << endl; // multiplication 
-cout << (i / j) << endl; // division 
-</code> 
- 
-Le rôle de ces opérateurs peut varier en fonction du type. Par exemple, l'opérateur ''+'' correspondra à une addition pour les types numériques et à une concaténation pour les chaînes de caractères ''string''. 
- 
-<code cpp main.cpp> 
-#include <iostream> 
-#include <string> 
- 
-int main() { 
-    int const i1 { 1 }; 
-    int const i2 { 2 }; 
-    std::cout << (i1 + i2) << std::endl; // addition 
-    std::string const s1 { "1" }; 
-    std::string const s2 { "2" }; 
-    std::cout << (s1 + s2) << std::endl; // concaténation 
-} 
-</code> 
- 
-affiche : 
- 
-<code> 
-3 
-12 
-</code> 
- 
-De plus, selon les types, tous les opérateurs n'ont pas forcement un sens. Par exemple, pour les chaînes ''string'', seul l'opérateur ''+'' a un sens, les autres opérateurs ne sont pas définis. Pour les collections (''vector'', ''array'', etc.), l'opérateur ''+'' n'est pas défini (c'est un choix des concepteurs, ils auraient pu définir l'opérateur ''+'' pour fussionner deux collections, mais pour des raisons de sémantique, ce n'est pas le cas). 
- 
-<note info>Les deux grands types de classes sont les classes à sémantique de valeur (que vous avez vu dans ce chapitre) et les classes à sémantique d'entité. Vous apprendrez dans la partie "programmation orientée objet" comment créer ces types de classes. Pour les distinguer, le plus simple est de se poser des questions sur ce que peut faire une classe ou non. 
- 
-  * Est-ce que cela à un sens de pouvoir copier un objet ? 
-  * Est-ce que cela à un sens de tester si deux objets sont égaux ? 
-  * Est-ce que cela à un sens d’additionner deux objets ? 
- 
-La majorité des classes de la bibliothèque standard sont à sémantique de valeur. Vous verrez dans la partie sur la programmation objet comment identifier et créer des classes à sémantique d'entité.</note> 
- 
-===== Les algorithmes d'égalité ===== 
- 
-Si vous souhaitez comparer deux sous-ensembles de collections ou si vous souhaitez utiliser un prédicat différent, il ne sera pas possible d'utiliser l'opérateur d'égalité ''==''. Dans ce cas, la bibliothèque standard fournit l'algorithme ''std::equal'' pour comparer si deux collections sont identiques ou non. 
- 
-Lorsque l'on découvre une nouvelle fonction, la première chose à faire est de jetter un coup d'oeil sur la documentation : [[http://en.cppreference.com/w/cpp/algorithm/equa|std::equal]]. Cette page nous apprend plusieurs choses : 
- 
-  * cet algorithme est défini dans le fichier d'en-tête ''<algorithm>'', il faudra donc l'inclure dans votre code pour utiliser ''equal'' ; 
-  * il existe quatre versions de cet algorithme :  
-    * le premier prend en argument le début et la fin d'une première collection et le début d'une seconde collection ; 
-    * le second est similaire au premier, avec un prédicat personnalisé ; 
-    * la troisième prend en argument le début et la fin de la première collection, puis de la seconde ; 
-    * le quatrième est similaire au troisième, avec un prédicat personnalisé. 
-  * le prédicat doit respecter la signature suivante : ''bool pred(const Type1 &a, const Type2 &b);'' (c'est donc une fonction binaire - une fonction qui prend deux arguments - et retourne un booléen). 
- 
-La page de documentation donne également des codes d'exemple d'utilisation de ces fonctions. 
- 
-==== Comparer des collections différentes ==== 
- 
-Comme cela a été dit auparavant, la classe ''string'' peut être vu comme une collection de caractères (''char''). Cependant, si on essaie de comparer une variable de type ''string'' à une tableau de caractères, on obtient une erreur : 
- 
-<code cpp main.cpp> 
-#include <iostream> 
-#include <string> 
-#include <vector> 
-#include <algorithm> 
- 
-int main() { 
-    std::string const s { "abcdef" }; 
-    std::vector<char> const a { 'a', 'b', 'c', 'd', 'e', 'f' }; 
-    std::cout << std::boolalpha; 
-    std::cout << (s == a) << std::endl; 
-} 
-</code> 
- 
-affiche l'erreur à la compilation suivante : 
- 
-<code> 
-main.cpp:10:21: error: invalid operands to binary expression ('const std::string'  
-(aka 'const basic_string<char, char_traits<char>, allocator<char> >') and  
-'const std::vector<char>') 
-    std::cout << (s == a) << std::endl; 
-                  ~ ^  ~ 
-1 error generated. 
-</code> 
- 
-Cette erreur signifie que le compilateur ne trouve pas d'opérateur d'égalité ''=='' permettant de comparer un type ''string'' avec un type ''vector<char>''. En effet, les opérateurs de comparaison sont définis pour accepter deux arguments de même type, par exemple deux ''string'' ou deux ''vector<char>'', mais pas deux collection de type différents, même si ces deux types correspondent tous deux à des collections de ''char'' (on parle parfois de typage "fort" du C++). 
- 
-Dans cette situation, il est possible d'utiliser la fonction ''equal'' pour tester l'égalité de deux collections. Cet algorithme fonctionne sur des collections de types différents, à partir du moment où les éléments sont comparable par égalité. Par exemple, il sera possible de comparer des ''string'' et des ''vector<char>'' (les éléments sont dans les deux cas des ''char'') ou des ''vector<int>'' et ''vector<float>'' (il est possible de comparer un entier et un nombre réel), mais pas ''vector<int>'' et ''vector<string>'' (''int'' et ''string'' ne sont pas comparable par égalité). 
- 
-En utilisant ''std::equal'', la comparaison devient : 
- 
-<code cpp main.cpp> 
-#include <iostream> 
-#include <string> 
-#include <vector> 
-#include <algorithm> 
- 
-int main() { 
-    std::string const s { "abcdef" }; 
-    std::vector<char> const v1 { 'a', 'b', 'c', 'd', 'e', 'f' }; 
-    std::vector<char> const v2 { 'a', 'z', 'e', 'r', 't', 'y' }; 
-    std::cout << std::boolalpha; 
-    std::cout << std::equal(begin(s), end(s), begin(v1)) << std::endl; 
-    std::cout << std::equal(begin(s), end(s), begin(v2), end(v2)) << std::endl; 
-} 
-</code> 
- 
-affiche : 
- 
-<code> 
-true 
-false 
-</code> 
- 
-==== Comparer des parties de collections ==== 
- 
- 
- 
-<code cpp main.cpp> 
-#include <iostream> 
-#include <string> 
-#include <algorithm> 
- 
-int main() { 
-    std::string const s1 { "abcdef" }; 
-    std::string const s2 { "abcdEF" }; 
-    std::cout << std::boolalpha; 
-    std::cout << std::equal(begin(s1), end(s1), begin(s2)) << std::endl; 
-} 
-</code> 
- 
-exemple avec prédicat, pour ignorer la casse 
- 
-<code cpp> 
-#include <iostream> 
-#include <string> 
-#include <algorithm> 
-#include <cctype> 
- 
-int main() { 
-    std::string const s1 { "abcdef" }; 
-    std::string const s2 { "abcdEF" }; 
-    std::cout << std::boolalpha; 
-    std::cout << std::equal(begin(s1), end(s1), begin(s2),  
-        [](auto lhs, auto rhs){ return std::toupper(lhs) == std::toupper(rhs); })  
-        << std::endl; 
-} 
-</code> 
- 
-==== Exercices ==== 
- 
-  * comparer vector<int> et vector<float> 
-  * comparer vector<int> et vector<string> (aide : utilise std::stoi) 
-  * comparer vector<int> et vector<string> (qui contient "un", "deux", etc.) 
- 
-===== Performances ===== 
- 
- 
- 
-==== Complexité algorithmique ==== 
- 
-Dans la page de documentation de [[http://en.cppreference.com/w/cpp/algorithm/equal|std::equal]], il y a un point dont on n'a pas parlé. Il y a une partie appelée "Complexity", qui décrit la complexité algorithmique de ''equal''. La complexité est une forme de mesure de la performance d'un algorithme. C'est une notion importante à connaître, mais il faut aussi connaître ses limites. 
- 
-notation big O 
- 
-==== Mesurer les temps d'exécution ==== 
- 
-std::chrono 
- 
- 
-^ Chapitre précédent ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ Chapitre suivant ^ 
- 
-{{tag> Cours C++}} 
comparer_strings.1409610195.txt.gz · Dernière modification: 2014/09/02 00:23 par gbdivers