programmez-en-d/expressions_logiques.whata

305 lines
17 KiB
Plaintext

[set
title = "Expressions logiques"
partAs = chapitre
translator = "Raphaël Jakse"
proofreader = "Stéphane Goujet"
]
Le travail qu'un programme réalise est accompli par des expressions. Toute partie d'un programme qui produit une valeur ou un effet de bord est appelée une [* expression]. La définition d'expression est très large car même une constante comme 42 et une chaîne comme ``"bonjour"`` sont des expressions parce qu'elles produisent respectivement les valeurs constantes ``42`` et ``"bonjour"``.
[p Note~ : | ne confondez pas [* produire une valeur] avec [* définir une variable]. Les valeurs n'ont pas besoin d'être associées à des variables.]
Les appels de fonctions comme [c writeln] sont également des expressions, parce qu'ils ont des effets de bords. Dans le cas de [c writeln], l'effet se produit sur le flux de sortie et se traduit par l'ajout de caractères dans ce flux. Un autre exemple tiré des programmes que nous avons écrits serait l'opération d'affectation, qui affecte la variable à sa gauche.
Parce qu'elles produisent des valeurs, les expressions peuvent faire partie d'autre expressions. Ceci nous permet de former des expressions plus complexes à partir d'expressions plus simples. Par exemple, en supposant qu'il y a une fonction nommée [c TemperatureActuelle()] produisant la valeur de la température actuelle de l'air, la valeur qu'elle produit peut être directement utilisée dans une expression impliquant [c writeln].
[code=d <<<
writeln("Il fait actuellement ", TemperatureActuelle(),
" degrés.");
>>>]
Cette ligne consiste en 4 expressions~ :
- [c=d "Il fait actuellement "]
- [c=d TemperatureActuelle()]
- [c=d " degrés."]
- l'expression [c=d writeln] qui utilise les trois autres expressions.
Dans ce chapitre nous allons voir un genre particulier d'expressions qui sont utilisées dans les instructions conditionnelles.
Avant d'aller plus loin, je voudrais faire un petit rappel sur l'opérateur d'affectation, en mettant cette fois l'accent sur les deux expressions qui apparaissent à sa gauche et à sa droite~ : l'opérateur d'affectation (=) affecte la valeur de l'expression de droite à l'expression de gauche (p. ex.~ : à une variable).
[code=d <<<
temperature = 23 // La valeur de temperature devient 23
>>>]
[ = Expressions logiques
Les expressions logiques sont les expressions qui sont utilisées en arithmétique booléenne. Les expressions logiques sont ce qui fait qu'un programme prend des décisions comme «~ si la réponse est [i oui], j'enregistrerai ce fichier~ ».
Les expressions logiques peuvent prendre une valeur parmi seulement deux possibles~ : [c false]([* faux]) qui indique que l'expression est fausse, et [c true]([* vrai]) qui indique que l'expression est vraie.
J'utiliserai des expressions [c writeln] dans les exemples qui suivent. Si une ligne se finit par [c true], cela voudra dire que ce qui est affiché sur la ligne est vrai. De même, [c false] voudra dire que ce qui est sur la ligne est faux. Par exemple, si la sortie d'un programme est la suivante~ :
[output <<<
Il y a du café : true
>>>]
Cela voudra dire qu'«~ il y a du café~ ». De même,
[output <<<
Il y a du café : false
>>>]
voudra dire qu'«~ il n'y a pas de café~ ». Notez que le fait que «~ il y a~ » apparaît sur la gauche de la ligne ne veut pas dire que le café existe. J'utilise la construction «~ Il y a...~ : false~ » pour dire «~ il n'y a pas~ » ou «~ c'est faux~ ».
Les expressions logiques sont utilisées de façon intensive dans les instructions conditionnelles, les boucles, les paramètres de fonctions, etc. Il est essentiel de comprendre comment elles fonctionnent. Par chance, les expressions logiques sont très faciles à expliquer et utiliser.
Les opérateurs logiques qui sont utilisés dans les expressions logiques sont les suivants~ :
- [ L'opérateur [c=d ==] répond à la question «~ est égal à~ ?~ ». Il compare les deux expressions à sa droite et à sa gauche et produit [c true] si elles sont égales et [c false] si elle ne sont pas égales. Par définition, la valeur que [c=d ==] produit est une expression logique.
Par exemple, supposons que nous avons les deux variables suivantes~ :
[code=d <<<
int joursDansUneSemaine = 7;
int moisDansUneAnnee = 12;
>>>]
Les expressions suivantes sont deux expressions logiques qui utilisent ces valeurs~ :
[code=d <<<
joursDansUneSemaine == 7 // true
moisDansUneAnnee == 11 // false
>>>]
]
- [ L'opérateur [c=d !=] répond à la question «~ n'est pas égal à~ ?~ ». Il compare les deux expressions à ses côtés et produit l'opposé de [c=d <<<==>>>].
[code=d <<<
joursDansUneSemaine != 7 // false
moisDansUneAnnee != 11 // true
>>>]
]
- [ l'opérateur [c=d > ||] veut dire «~ ou~ » et produit [c=d true] si l'une ou l'autre des expressions qui sont à ses cotés est vraie.
Si la valeur de l'expression de gauche est [c=d true], il produit la valeur [c true] sans même regarder l'autre expression. Si l'expression du côté gauche est fausse, alors l'opérateur produit la valeur de l'expression de droite. Cet opérateur est similaire à la conjonction «~ ou~ » du français dans le sens où si l'une, l'autre ou les deux expressions sont vraies, alors il produit [c true].
Toutes les valeurs possibles des deux côtés de cet opérateur et leur résultat sont tels que présentés dans cette table de vérité~ :
|= Expression de gauche |= Expression de droite |= Résultat
| [c=d false] | [c=d false] | [c=d false]
| [c=d false] | [c=d true] | [c=d true]
| [c=d true] | [c=d false] (non évalué) | [c=d true]
| [c=d true] | [c=d true] (non évalué) | [c=d true]
[code=d <<<
import std.stdio;
void main()
{
// false veut dire «~ non~ », true veut dire «~ oui~ »
bool ilExisteDuCafé = false;
bool ilExisteDuThé = true;
writeln("Il y a une boisson chaude~ : ",
ilExisteDuCafé || ilExisteDuThé);
}
>>>]
Du fait qu'une des deux expressions est vraie, l'expression logique de ce programme produit [c=d true].
]
- [ L'opérateur [c=d &&] signifie «~ et~ », et produit [c true] si les deux expressions sont vraies.
Si la valeur de l'expression de gauche est fausse, il produit [c false] sans même regarder l'expression qui est à sa droite. Si l'expression de gauche est vraie, alors il produit la valeur de l'expression de droite. Cet opérateur est similaire à la conjonction «~ et~ » du français~ : si la valeur de gauche et la valeur de droite sont toutes les deux [c true], alors il produit [c true].
|= Expression de gauche |= Expression de droite |= Résultat
| [c false] | [c false] (non évalué) | [c false]
| [c false] | [c true] (non évalué) | [c false]
| [c true] | [c false] | [c false]
| [c true] | [c true] | [c true]
[code=d <<<
writeln("Je vais boire du café: ",
jeVeuxBoireDuCafé && ilExisteDuCafé);
>>>]
Note~ : Le fait que les opérateurs [c > ||] et [c &&] peuvent ne pas évaluer l'expression de droite est appelé «~ comportement raccourci~ ». Le seul autre opérateur qui a ce comportement est l'opérateur ternaire [c ?:], qui sera vu dans un chapitre prochain. Tous les autres opérateurs évaluent et utilisent toujours toutes leurs expressions.
]
- [ L'opérateur [c ^] répond à la question «~ l'un ou l'autre~ ?~ ». Cet opérateur produit [c true] si seule une expression est vraie, mais pas les deux.
|= Expression de gauche |= Expression de droite |= Résultat
| [c false] | [c false] | [c false]
| [c false] | [c true] | [c true]
| [c true] | [c false] | [c true]
| [c true] | [c true] | [c false]
Par exemple, la logique qui représente mon choix de jouer aux échecs si un seul de mes deux amis arrive peut être codé ainsi~ :
[code=d writeln("Je jouerai aux échecs~ : ", jimEstApparu ^ bobEstApparu);]
]
- [ L'opérateur [c > <] répond à la question «~ est plus petit que~ ?~ » (ou «~ vient avant dans l'ordre de tri~ ?~ »).
[code=d writeln("Nous gagnons~ : ", leurScore < notreScore);]
]
- [ L'opérateur [c > >] répond à la question « ~ est plus quand que~ ?~ » (ou «~ vient après dans l'ordre de tri~ ?~ »).
[code=d writeln("Ils gagnent~ : ", leurScore > notreScore);]
]
- [ L'opérateur [c <=] répond à la question «~ est inférieur ou égal~ ?~ » (ou «~ vient avant ou au même endroit que~ ?~ »). Cet opérateur est l'opposé de l'opérateur [c > >].
[code=d <<<
writeln("Nous n'avons pas été vaincus~ : ", leurScore <= notreScore);]
>>>]
- [ L'opérateur [c > >=] répond à la question «~ est supérieur ou égal à~ ?~ » (ou «~ vient après ou au même endroit que~ ?~ »). Cet opérateur est l'opposé de l'opérateur [c > <].
[code=d writeln("Nous n'avons pas gagné~ : ", leurScore >= notreScore);]
]
- [ L'opérateur [c !] veut dire «~ l'opposé de~ ». Différent des autres opérateurs logiques, il prend une seule expression et produit [c true] si cette expression est fausse, et [c false] si cette expression est vraie.
[code=d writeln("Je marcherai~ : ", !ilExisteUnVélo);]
]
]
[ = Grouper les expressions
L'ordre dans lequel les expressions sont évaluées peut être indiqué en utilisant des parenthèses pour les grouper. Quand des expressions entre parenthèses apparaissent dans des expressions plus complexes, les expressions entres parenthèses sont évaluées avant qu'elles puissent être utilisées dans les expressions dans lesquelles elles apparaissent. Par exemple, l'expression «~ s'il y a du café ou du thé, et aussi des cookies ou des pains au lait, alors je suis content~ » peut être codé de cette manière~ :
[code=d <<<
writeln("Je suis content~ : ",
(ilExisteDuCafé || ilExisteDuThé) && (ilExisteDesCookies || ilExisteDesPains));
>>>]
Si les sous-expressions n'étaient pas entre parenthèses, l'expression serait évaluée selon les règles de priorité opératoire du D (qui ont été héritées du C). Comme dans ces règles [c &&] a une plus grande priorité que [c > ||], écrire l'expression sans parenthèses ne serait pas évalué comme voulu~ :
[code=d <<<
writeln("Je suis content~ : ",
ilExisteDuCafé || ilExisteDuThé && ilExisteDesCookies || ilExisteDesPains);
>>>]
L'opérateur [c &&] serait évalué en premier et l'expression entière aurait un sens équivalent à l'expression suivante~ :
[code=d <<<
writeln("Je suis content~ : ",
ilExisteDuCafé || (ilExisteDuThé && ilExisteDesCookies) || ilExisteDesPains);
>>>]
Cela a un sens complètement différent~ : «~ s'il y a du café, ou du thé et des cookies, ou du pain~ ; alors je suis content~ ».
]
[ = Lire un booléen en entrée
Toutes les valeurs booléennes ci-dessus sont automatiquement affichées comme [c false] et [c true]. Ce n'est pas le cas dans la direction opposée~ : les chaînes [c "false"] et [c "true"] ne sont pas automatiquement lues comme valeurs [c false] et [c true]. Pour cette raison, l'entrée doit être d'abord lue en tant que chaîne et convertie vers une valeur booléenne.
Comme l'un des exercices suivants vous demande d'entrer [c "false"] et [c "true"] depuis l'entrée standard, j'ai été obligé d'utiliser des fonctionnalités du D que je ne vous ai pas encore expliquées. Je vais devoir définir une fonction qui convertit la chaîne en entrée en valeur booléenne. Cette fonction effectuera cette tâche en appelant [c to], qui est défini dans le module [c std.conv]. (Vous pourrez voir des erreurs [c ConvException] si vous entrez autre chose que [c "false"] ou [c "true"].)
J'espère que tout le code qui est dans les fonctions principales des programmes suivants est clair. [c read_bool()] est la fonction qui contient de nouvelles fonctionnalités. Bien que j'aie inséré des commentaires pour expliquer ce qu'elle fait, vous pouvez ignorer cette fonction pour le moment. Malgré tout, elle est nécessaire pour que le programme marche et compile correctement.
]
[ = Exercices
# [ Nous avons vu ci-dessus que les opérateurs [c > <] et [c > >] sont utilisés pour déterminer si une valeur est inférieure ou supérieure à une autre valeur~ ; mais il n'y a pas d'opérateur qui réponde à la question «~ est entre~ ?~ » pour déterminer si une valeur est entre deux autres valeurs.
Supposons qu'un programmeur a écrit le code pour déterminer si une valeur est entre 10 et 20. Observez que le programme ne peut pas être compilé tel quel~ :
[code=d <<<
import std.stdio;
void main()
{
int valeur = 15;
writeln("est entre~ : ",
10 < valeur < 20); // ← ERREUR de compilation
}
>>>]
Essayez d'utiliser des parenthèses autour de l'expression entière~ :
[code=d <<<
writeln("Est entre~ : ",
(10 < valeur < 20)); // ← ERREUR de compilation
>>>]
Observez qu'il ne peut toujours pas être compilé.
]
# [
Alors qu'il cherchait une solution à ce problème, le même programmeur découvre que l'utilisation des parenthèses permet maintenant la compilation du code~ :
[code=d <<<
writeln("Est entre~ : ",
(10 < valeur) < 20); // ← compile mais FAUX
>>>]
Observez que le programme marche maintenant comme attendu et affiche [c "true"]. Malheureusement, la sortie est trompeuse parce que le programme a un bogue. Pour voir les effets de ce bogue, remplacez 15 avec une valeur supérieure à 20~ :
[code=d int valeur = 21;]
Observez que le programme affiche toujours [c "true"] même si 21 n'est pas plus petit que 20.
Astuce~ : se souvenir que le type d'une expression logique est [c bool]. Vérifier qu'une valeur booléenne est inférieure à 20 ne devrait pas avoir de sens.
]
# [ L'expression logique qui répond à la question «~ est entre~ ?~ » doit plutôt être codée comme ceci~ : «~ est plus grand que la petite valeur et plus petit que la grande valeur~ ?~ »
Changez l'expression du programme selon cette logique et observez que ça affiche maintenant [c "true"] comme attendu. Vérifiez également que l'expression logique marche correctement pour d'autres valeurs. Par exemple, quand la valeur est 50 ou 1, le programme devrait afficher [c "false"]~ ; et quand c'est 12, le programme devrait afficher [c "true"].
]
# [
Supposons que nous pouvons aller à la plage lorsque l'une des conditions suivantes est satisfaite~ :
- si la distance jusqu'à la plage est inférieure à 10 et il y a un vélo pour tout le monde~ ;
- si nous somme moins de 6, et nous avons une voiture, et l'un de nous a le permis de conduire.
Tel quel, le programme suivant écrit toujours "[c true]". Écrivez une expression logique qui affichera "[c true]" quand l'une des conditions ci-dessus est vraie. (Lors de l'essai du programme, entrez "false" ou "true" pour les questions qui commencent par «~ Y a-t-il~ ».). N'oubliez pas d'inclure la fonction [c read_bool()] quand vous testerez le programme suivant~ :
[code=d <<<
import std.stdio;
import std.conv;
import std.string;
void main()
{
write("Combien sommes-nous~ ? ");
int nombrePersonnes;
readf(" %s", &nombrePersonnes);
write("Combien y a-t-il de vélos~ ? ");
int nombreVélos;
readf(" %s", &nombreVélos);
write("À quelle distance est la mer~ ? ");
int distance;
readf(" %s", &distance);
bool existeUneVoiture = read_bool("Y a-t-il une voiture~ ? ");
bool existeUnPermis =
read_bool("Y a-t-il un permis de conduire~ ? ");
/*
Remplacez la valeur 'true' ci-dessous par une expression logique qui
produit la valeur true quand l'une des conditions
listées dans la question est satisfaite~ :
*/
writeln("Nous allons à la mer~ : ", true);
}
/*
Cette fonction utilise des fonctionnalités qui seront
expliquées dans des chapitres suivant.
*/
bool read_bool(string message)
{
// Afficher le message
write(message, "(false ou true) ");
// Lire une ligne comme une chaîne
string input;
while (input.longueur == 0) {
input = chomp(readln());
}
// Produire une valeur 'bool' depuis cette chaîne
bool résultat = to!bool(input);
// Retourner le résultat
return résultat;
}
>>>]
Entrez des valeurs diverses et vérifiez que l'expression logique que vous avez écrite fonctionne correctement.
]
[[part:corrections/expressions_logiques | …~ Les solutions]]
]