Nous avons vu, dans le chapitre précédent, l'existence des instructions IF/ELSIF/CASE
. Mais nous sommes tombés sur un os : nos codes ne prenaient pas en compte les majuscules. Je vous propose donc de repartir du début pour simplifier votre travail de compréhesion. Notre code se résumera donc à la version suivante.
1 2 3 4 | if Reponse = 'o' then Put("Vous avez bien de la chance. ") ; else Put("Ah… Dommage.") ; end if ; |
Voyons maintenant comment gérer les majuscules sans nous répéter.
- Introduction aux booléens
- La négation avec Not
- Les opérations Or et And
- L'opération XOR (optionnel)
- Exercice
- Priorités booléennes et ordre de test (Supplément)
Introduction aux booléens
Un bref historique
L'algèbre booléenne ou algèbre de Boole (du nom du Mathématicien George Boole) est une branche des mathématiques traitant des opérations logiques : on ne manipule plus des nombres mais des propositions qui peuvent être vraies ou fausses. Elle a été très utile en électronique (c'est grâce à elle que nos bêtes ordinateurs savent aujourd'hui additionner, multiplier…) et va nous servir aujourd'hui en programmation.
Mais… euh… Je suis nul(le) en Maths.
Pas de problème! Il ne s'agit que de logique donc il n'y aura pas de calcul (au sens habituel du terme). De plus, nous ne ferons qu'effleurer ce pan des Mathématiques.
Qu'est-ce qu'un booléen ?
Tout d'abord, lorsque l'on écrit :
1 | if Reponse = 'o' |
…, il n'y a que deux alternatives possibles : ou cette affirmation est VRAIE (Reponse vaut bien 'o'
) ou bien elle est FAUSSE (Reponse vaut autre chose). Ces deux alternatives (VRAI/FAUX) sont les deux seules valeurs utilisées en algèbre booléenne. En Ada, elles se notent TRUE
(VRAI) et FALSE
(FAUX). Un booléen est donc un objet qui vaut soit VRAI, soit FAUX. En Ada, cela se déclare de la manière suivante :
1 2 | A : boolean := TRUE ; B : boolean := FALSE ; |
Il est également possible d'affecter à nos variables booléennes le résultat d'un prédicat.
1 | A := (Reponse = 'o') |
Hein ? La variable A prend la valeur contenue dans Reponse ou bien il prend la valeur 'o'
? Et puis, c'était pas sensé valoir TRUE ou FALSE ?
La variable A prendra bien comme résultat TRUE
ou FALSE
et sûrement pas 'o'
! Tout dépend de l'égalité entre parenthèses : si Reponse est bien égal à 'o'
, alors la variable A vaudra TRUE
, sinon il vaudra FALSE
. Il sera ensuite possible d'utiliser notre booléen A dans nos conditions de la façon suivante :
1 2 3 | if A then... end if ; |
Cela signifie «Si A est vrai alors…». La variable A remplacera ainsi notre égalité. Par conséquent, il est généralement judicieux de nommer les booléens à l'aide d'adjectifs qualificatifs comme Actif, Present, Majeur ou Est_Majeur … L'instruction « IF Majeur
… » se comprend ainsi plus aisément que « IF Majeur = TRUE
… »
Les opérateurs booléen
Voici les quelques opérations booléennes que nous allons aborder :
Opérateur |
Traduction littérale |
Signification |
---|---|---|
Not |
Non |
Not A : «il faut que A ne soit pas vrai» |
Or |
Ou |
A or B : «il faut que A ou B soit vrai» |
And |
Et |
A And B : «il faut que A et B soient vrais» |
Xor |
Ou exclusif |
A xor B : «il faut que A ou B soit vrai mais pas les deux.» |
Ce ne sont pas les seuls opérateurs existant en algèbre booléenne, mais ce sont ceux que vous serez amenés à utiliser en Ada.
La négation avec Not
Revenons à notre programme. Nous voudrions inverser les deux instructions d'affichage de la manière suivante :
1 2 3 4 | if Reponse = 'o' then Put("Ah… Dommage.") ; else Put("Vous avez bien de la chance. ") ; end if ; |
Chacun comprend que notre programme ne répond plus correctement : si l'utilisateur a plusieurs ordinateurs, le programme lui répond «dommage» !? Nous devons donc changer la condition : le programme doit afficher «Dommage» seulement SI Reponse ne vaut pas 'o'
! La négation d'une instruction booléenne se fait à l'aide de l'instruction NOT
. D'où le code suivant :
1 2 3 4 | if Not Reponse = 'o' -- si la réponse n'est pas Oui then Put("Ah… Dommage.") ; -- alors on affiche «Dommage» else Put("Vous avez bien de la chance. ") ; -- sinon on affiche le second message end if ; |
Le programme fait ainsi de nouveau ce qu'on lui demande. Retenez cette astuce car il arrive souvent que l'on se trompe dans la condition ou l'ordre des instructions entre THEN
et ELSE
, notamment quand les conditions deviennent plus complexes. De plus, vous pouvez être amenés à ne tester que le cas ou une condition ne serait pas remplie, par exemple : « IF NOT Fichier_Existant
… »
Enfin, nous ne pouvons passer aux opérations suivantes sans faire un peu d'algèbre booléenne. Voici quelques résultats basiques auxquels je vous invite à réfléchir :
- Si A est vrai, alors Non A est faux. Si A = true alors Not A = false.
- Si A est faux, alors Non A est vrai. Si A = false alors Not A = true.
- Quelle que soit la valeur du booléen A, Non Non A = A (Not not A = A)
Les opérations Or et And
L'instruction OR
Nous avons vu dans la partie précédente que le code suivant ne gérait pas les majuscules :
1 2 3 4 | if Reponse = 'o' then Put("Vous avez bien de la chance. ") ; else Put("Ah… Dommage.") ; end if ; |
Il faudrait poser la condition «SI la réponse est o ou O». C'est-à-dire qu'il suffirait que l'une des deux conditions soit remplie pour que le programme affiche que vous avez de la chance. Pour cela, on utilise l'instruction OR
. Pour rappel, «OR» signifie «OU» en Français.
1 2 3 4 | if Reponse = 'o' or Reponse = 'O' then Put("Vous avez bien de la chance. ") ; else Put("Ah… Dommage.") ; end if ; |
Encore un peu d'algèbre booléenne : si A et B sont deux propositions (vraies ou fausses, peu importe), l'instruction « A ou B » ne sera vraie que dans les trois cas suivants :
- si A est vrai
- si B est vrai
- si les deux sont vrais
L'instruction AND
Nouveau jeu ! Inversons, comme dans la partie précédente, les instructions d'affichage. Nous devrions alors écrire une négation :
1 2 3 4 | if not(Reponse = 'o' or Reponse = 'O') then Put("Ah… Dommage.") ; else Put("Vous avez bien de la chance. ") ; end if ; |
Vous vous souvenez de la partie sur l'instruction NOT
? Nous avons fait la même chose : puisque l'on a inversé les instructions d'affichage, nous écrivons une négation dans le prédicat. Mais cette négation peut aussi s'écrire :
1 2 3 4 | if not Reponse = 'o' and not Reponse = 'O' then Put("Ah… Dommage.") ; else Put("Vous avez bien de la chance. ") ; end if ; |
L'opérateur AND
signifie ET. Il implique que les deux conditions doivent être remplies en même temps : Reponse ne vaut pas 'o'
ET Reponse ne vaut pas non plus 'O'
. Si une seule des deux conditions n'était pas remplie, l'ensemble serait faux. Pratiquons encore un peu d'algèbre booléenne pour mieux comprendre :
- si A est VRAI et B est FAUX alors «A et B» est FAUX
- si A est VRAI et B est VRAI alors «A et B» est VRAI
- si A est FAUX et B est FAUX alors «A et B» est FAUX
De manière schématique, retenez que la négation d'un OU revient à signifier ET. Le contraire de VRAI OU VRAI est donc FAUX ET FAUX. Bon, je crois qu'il est temps d'arrêter car certains méninges doivent commencer à griller. Je vous invite donc à méditer ce que vous venez de lire.
L'opération XOR (optionnel)
Il nous reste une opération à voir, l'opération XOR
. Nous ne nous attarderons pas dessus car je préfèrerais que vous reteniez déjà ce que nous venons de voir sur NOT
, OR
et AND
. Toutefois, si vous voulez en savoir plus, rappelons que la phrase «A ou B» sera vraie dans trois cas :
- si A est vrai
- si B est vrai
- si A et B sont vrais tous les deux
Or, il existe des cas où il ne faut pas que les deux conditions soient vraies en même temps. Au lieu d'écrire «(A ou B) et pas (A et B)», il existe l'opération OU EXCLUSIF (XOR
). Pour que «A XOR B
» soit vrai, il faut que :
- A soit vrai
- ou que B soit vrai
- mais pas les deux en même temps !
Prenons un exemple. Vous rédigez un programme à destination des négociants (en fruits et légumes ou en matériaux de construction, peu importe). Ceux-ci ont besoin de nouer des contacts avec diverses entreprises, qu'elles soient productrices ou consommatrices de biens. Votre programme a pour but de rechercher de nouveaux clients ou fournisseurs ; il doit donc disposer de deux variables booléennes : Producteur
et Consommateur
qui indique le statut d'une entreprise. Il dispose également de deux instructions : Faire_Affaire
et Laisser_Tomber
.
Voici la première idée que nous pourrions avoir (attention, elle comporte une erreur) :
1 2 3 4 | IF Producteur OR Consommateur THEN Faire_Affaire ; ELSE Laisser_Tomber ; END IF ; |
On ne contacte une entreprise que si elle est productrice de bien OU consommatrice. Sauf que ce code possède une faille : si l'entreprise a fait le choix de produire elle-même les biens qu'elle consomme, quel intérêt avons-nous à jouer les négociants ? Il faut donc retirer le cas où une entreprise est à la fois productrice et consommatrice. C'est là que XOR
entre en scène :
1 2 3 4 | IF Producteur XOR Consommateur --on ne retient plus les entreprises en circuit interne THEN Faire_Affaire ; ELSE Laisser_Tomber ; END IF ; |
Exercice
Revenons à notre problème initial : maintenant que nous avons vu les opérateurs booléens, vous pourriez reprendre votre programme nommé Questionnaire pour qu'il puisse gérer aussi bien les majuscules que les minuscules. Vous allez pouvoir compléter le code du précédent chapitre mais attention, il ne doit pas y avoir de redondance ! Hors de question d'écrire plusieurs fois la même instruction. Vous allez devoir utiliser l'un des opérateurs booléens vus dans ce chapitre. Oui, mais lequel ? À quelle opération cela correspond-il : OU, ET, NON ?
Voici une première solution avec des ELSIF
:
1 2 3 4 5 6 7 8 9 10 | if Reponse = 'o' or Reponse = 'O' then Put("Vous avez bien de la chance.") ; elsif Reponse = 'n' or Reponse = 'N' then Put("Ah… dommage. ") ; elsif Reponse = 'p' or Reponse = 'P' then Put("Reponses normandes non valides") ; elsif Reponse = 'f' or Reponse = 'F' then Put("J'aurais pas du apprendre l'allemand…") ; else Put("C'est pas une reponse.") ; end if ; |
Comme vous avez du vous en douter, il suffit juste de compléter les blocs IF/ELSIF
avec une instruction OR
. Il est évident que votre variable réponse ne peut être en majuscule ET en minuscule en même temps.
Voici une seconde solution avec un CASE
(un poil plus dur car la solution proposée comporte une astuce) :
1 2 3 4 5 6 7 | case Reponse is when 'o' | 'O' => Put("Vous avez bien de la chance.") ; -- si oui when 'n' | 'N' => Put("Ah… dommage. ") ; -- si non when 'p' | 'P' => Put("Reponses normandes non valides") ; -- si peut-être when 'f' | 'F' => Put("J'aurais pas du apprendre l'allemand…") ; -- si "not French" when others => Put("C'est pas une reponse.") ; -- si autre chose end case ; |
Durant vos essais, vous avez du remarquer que lors de l'instruction WHEN
il n'était pas possible de faire appel aux opérateurs booléens. Pour remplacer l'instruction OR
, il suffit d'utiliser le symbole «|» (Alt gr + le chiffre 6). Ce symbole se nomme Pipe (prononcez « Païpe », c'est de l'Anglais). Oui, je sais vous ne pouviez pas le trouver par vous-même.
Priorités booléennes et ordre de test (Supplément)
Cette ultime partie doit être vue comme un complément. Elle est plutôt théorique et s'adresse à ceux qui maîtrisent ce qui précède (ou croient le maîtriser ). Si vous débutez, jetez-y tout de même un œil pour avoir une idée du sujet, mais ne vous affolez pas si vous ne comprenez rien ; cela ne vous empêchera pas de suivre la suite de ce cours et vous pourrez revenir dessus plus tard, lorsque vous aurez gagné en expérience.
Priorités avec les booléens
Nous avons déjà évoqué, dans le chapitre sur les variables et les opérations, la question des priorités dans un calcul. Par exemple, dans l'opération $10 +5 \times 3$ le résultat est…
Euh… 45 ?
Argh ! Non ! Le résultat est 25 ! En effet, on dit que la multiplication est prioritaire sur l'addition, ce qui signifie qu'elle doit être faite en premier (sauf indication contraire à l'aide de parenthèses qui, elles, sont prioritaires sur tout le monde). Et bien des règles de priorités existent également en algèbre booléenne. Découvrons-les à travers ces quelques exemples de la vie courante.
«je viendrai si Albert ET Bernard viennent OU si Clara vient»
Cela revient à écrire : « A AND B OR C
».
Mais que doit-on comprendre ? « (A AND B) OR C
» ou alors « A AND (B OR C)
» ? Quelle opération est prioritaire AND
ou OR
?
Réfléchissons à notre exemple, si Clara est la seule à venir, tant pis pour Albert et Bernard, je viens.
- Dans le cas «
(A AND B) OR C
» : les parenthèses m'obligent à commencer par leAND
. Or, A et B sont faux tous les deux (Albert et Bernard ne viennent pas) donc(A AND B)
est faux. Nous finissons avec leOR
: la première partie est fausse mais C est vrai (Clara vient pour notre plus grand bonheur), donc le résultat est Vrai ! Je viens ! A priori, cette écriture correspondrait. - Dans le cas «
A AND (B OR C)
» : les parenthèses m'obligent à commencer par leOR
. B est faux (Bernard ne vient pas) mais C est vrai (Clara vient ) donc(B OR C)
est vrai. Nous finissons avec leAND
: la deuxième partie est vraie et A est faux(Albert ne vient pas), donc le résultat est… Faux ! Je suis sensé ne pas venir ! Apparemment, cette écriture ne correspond pas à ce que l'on était sensé obtenir.
Conclusion ? Il faut commencer par le AND
. Si nous enlevons les parenthèses, alors AND
est prioritaire sur OR
. Le ET peut être comparé à la multiplication ; le OU peut être comparé à l'addition.
Pour ceux qui feraient (ou auraient fait) des probabilités au lycée, le ET peut-être assimilé à l'intersection $\cap$ et donc à la multiplication, tandis que le OU peut-être assimilé à l'union $\cup$ et donc à l'addition. Gardez ces liens en tête, cela vous facilitera l'apprentissage des formules ou de l'algèbre booléenne.
Et XOR
et NOT
?
XOR
a la même priorité que OR
, ce qui est logique. Pour NOT
, retenez qu'il ne s'applique qu'à un seul booléen, il est donc prioritaire sur les autres opérations. Si vous voulez qu'il s'applique à toute une expression, il faudra user des parenthèses.
Ordre de test
L'intérêt de IF
, n'est pas seulement de distinguer différents cas pour appliquer différents traitements. Il est également utile pour éviter des erreurs qui pourraient engendrer un plantage en bonne et due forme de votre programme. Par exemple, prenons une variable n de type natural : elle ne peut pas (et ne doit pas) être négative. Donc avant de faire une soustraction, on s'assure qu'elle est suffisamment grande :
1 2 3 | if n >= 5 then n := n - 5 ; end if ; |
Ce que nous allons voir ici a notamment pour intérêt d'éviter des tests qui engendreraient des erreurs. Nous reviendrons dessus durant les chapitres sur les tableaux et les pointeurs pour illustrer mon propos. Nous resterons ici dans la théorie. Supposons que nous voulions ordonner nos tests :
Vérifie si A est vrai ET SEULEMENT SI C'EST VRAI, vérifie si B est vrai
Quel intérêt ? Je ne rentrerai pas dans un exemple en programmation, mais prenons un exemple de la vie courante. Que vaut-il mieux faire?
Vérifier si ma sœur qui est ceinture noire de judo n'est pas sous la douche ET vérifier si il y a encore de l'eau chaude. ou Vérifier si ma sœur qui est ceinture noire de judo est sous la douche ET SEULEMENT SI ELLE N'EST PAS SOUS LA DOUCHE vérifier si il y a encore de l'eau chaude.
Les vicieux tenteront la première solution et finiront avec une pomme de douche greffée à la place de l'oreille. Les prudents opteront pour la deuxième solution. Autrement dit, en programmation, la première méthode peut occasionner un plantage, pas la seconde. Maintenant voyons comment implémenter cela en Ada :
1 2 3 4 | if A=true AND THEN B=true --remarque : il n'est pas utile d'écrire "=true" THEN ... ELSE ... END IF ; |
Ainsi, on ne testera le prédicat "B=true" que si auparavant A était vrai. Autre possibilité :
Vérifie si A est vrai OU SI CE N'EST PAS VRAI, vérifie si B est vrai
Exemple de la vie courante. Vaut-il mieux…
Vérifier si il y a des steaks hachés dans le frigo ou chez le voisin? ou Vérifier si il y a des steaks hachés dans le frigo ou, SI VRAIMENT ON A TOUT FINI HIER aller vérifier chez le voisin?
Je ne sais pas vous, mais avant d'aller demander au voisin, je regarde si je n'ai pas ce qu'il faut dans mon frigo. Idem en programmation, si l'on peut économiser du temps-processeur et de la mémoire, on ne s'en prive pas. Donc pour implémenter cela, nous écrirons :
1 2 3 4 | if A=true OR ELSE B=true --remarque : il n'est pas utile d'écrire "=true" THEN ... ELSE ... END IF ; |
Petite traduction pour les éternels anglophobes : AND THEN
= «et alors» / OR ELSE
= «et sinon»
Les mots réservés THEN
et ELSE
peuvent donc, s'ils sont combinés respectivement avec AND
et OR
, être utilisés au moment du prédicat. Cela vous montre toute la souplesse du langage Ada cachée sous l'apparente rigidité.
L'algèbre booléenne peut vite s'avérer compliquée, alors n'hésitez pas à faire des schémas ou des tableaux si vous commencez à vous perdre dans vos conditions : quand on débute, bon nombre de bogues proviennent de conditions mal ficelées. Malgré cette complexité, retenez l'existence de NOT/OR/AND
. Ces trois opérateurs vous serviront régulièrement et permettront d'effectuer des tests plus complexes.
En résumé :
- Une variable de type
Boolean
ne peut prendre que deux valeurs :TRUE
ouFALSE
. Ces valeurs peuvent être affectées à votre variable soit directement, soit par un prédicat (dont la valeur est en général inconnue du programmeur). - L'opérateur
OR
exige qu'au moins l'un des deux prédicats ou booléens soit vrai. L'opérateurAND
exige que les deux prédicats ou booléens soient vrais. L'opérateurXOR
exige qu'un seul des deux prédicats ou booléens soit vrai. - Les négations (
NOT
) sont à manier avec prudence et parcimonie car elles conduisent très vite à des formules booléennes complexes. Ainsi, nous avons vu que «NOT(A OR B)
» était identique à «(NOT A) AND (NOT B)
» ou encore que «(A OR B) AND NOT(A AND B)
» pouvait se simplifier en «A XOR B
» - Rappelez-vous que
AND
est prioritaire surOR
. En cas de doute, mieux vaut utiliser trop de parenthèses que de risquer le bogue.