pour mieux assimiler le cours, je me donne des exercices supplémentaires. En ce moment, je veux demander à un utilisateur d'entrer un pseudonyme, puis de confirmer son choix. Il peut répondre par OUI ou par NON ; s'il donne une autre réponse, la demande de confirmation doit se répéter en boucle.
J'ai obtenu un code qui marche, mais je ne comprends pas pourquoi il marche !
Mon problème de compréhension porte sur le while. Je pensais que, vu que la réponse doit être "OUI" OU "NON", il fallait utiliser l'opérateur ||. Le problème, avec cet opérateur, c'est qu'aucune réponse n'est reconnue comme valide (ni OUI, ni NON, ni rien d'autre). Du coup, un peu en désespoir de cause, j'ai tenté l'opérateur &&. Il s'avère que cet opérateur marche parfaitement. Mais je ne comprends pas pourquoi, puisque la réponse ne peut pas être "OUI" ET "NON" en même temps.
Est-ce que quelqu'un peut m'expliquer ?
Merci !
void choixPseudo(void)
{
char pseudo[20+1]; char confirmation[3+1]= {0}; //Réponse attendue : OUI ou NON -> 3 lettres + 1 caractère de fin de chaîne.
puts ("Choisissez votre pseudo (max. 20 caractères : )");
fgets (pseudo, 21, stdin);
viderBuffer(); //Cette fonction est déclarée par ailleurs et marche normalement.
printf("Votre pseudo est : %s", pseudo);
while (strcmp(confirmation, "OUI") != 0 && strcmp(confirmation, "NON") !=0) //J'ai bien inclus <string.h>.
{
puts("Votre pseudo vous convient-il ? Tapez OUI ou NON (majuscules uniquement)\n.");
fgets(confirmation, 4, stdin);
viderBuffer();
}
}
Ce genre de cas pose souvent problème. Même si les booléens sont assez faciles à comprendre, je pense qu'il faudrait plancher un peu l'algèbre de Bool et de De Morgan.
Dans ce cas il serait naturel de dire : "refaire tant que ne n'ai pas (oui ou non)", que l'on notera /(O+N). Ce qui est strictement égal à /O*/N (pas oui et pas non).
Un petit conseille quand on commence : il ne faut pas hésiter à s’éclaircir les idées en ajoutant des variables pour "découper" les calculs. Même encore, je le fais régulièrement quand mes équations commencent à prendre une certaine ampleur :
void choixPseudo(void)
{
char pseudo[20+1],
confirmation[3+1]= {0}; //Réponse attendue : OUI ou NON -> 3 lettres + 1 caractère de fin de chaîne.
puts ("Choisissez votre pseudo (max. 20 caractères : )");
fgets (pseudo, 21, stdin);
viderBuffer(); //Cette fonction est déclarée par ailleurs et marche normalement.
printf("Votre pseudo est : %s", pseudo);
bool oui = false,
non = false,
reponseValide = false;
while (!oui && !non) // <=> while( !reponseValide) <- plus proche du langage humain
{
puts("Votre pseudo vous convient-il ? Tapez OUI ou NON (majuscules uniquement)\n.");
fgets(confirmation, 4, stdin);
viderBuffer();
oui = !strcmp(confirmation, "OUI");
non = !strcmp(confirmation, "NON");
reponseValide = oui || non;
}
}
Mon problème de compréhension porte sur le while. Je pensais que, vu que la réponse doit être "OUI" OU "NON", il fallait utiliser l'opérateur ||. Le problème, avec cet opérateur, c'est qu'aucune réponse n'est reconnue comme valide (ni OUI, ni NON, ni rien d'autre). Du coup, un peu en désespoir de cause, j'ai tenté l'opérateur &&. Il s'avère que cet opérateur marche parfaitement. Mais je ne comprends pas pourquoi, puisque la réponse ne peut pas être "OUI" ET "NON" en même temps.
Voici comment j'expliquerai cette difficulté classique :
− Si on veut tester que la réponse est "oui ou non", on utilise en effet OU :
FAIRE
instructions
JUSQU'À reponse = "oui" OU reponse = "non" // oui ou non ?
− Mais ici il s'agit de tester la négation. Or la négation de "oui ou non", c'est "ni oui, ni non", c'est-à-dire "ni oui et ni non" puisque la virgule signifie « et ». Il existait autrefois un jeu appelé le "ni oui, ni non", pour ceux qui connaissent ça peut peut-être aider...
Donc :
FAIRE
instructions
TANT QUE reponse != "oui" ET reponse != "non" // ni oui, ni non
Merci pour vos réponses, ça m'aide déjà à voir plus clair.
(a)
Du coup, je vais essayer d'appliquer le même raisonnement pour comprendre pourquoi l'opérateur || ne fonctionne pas (corrigez-moi si je me trompe). Apparemment, il s'agirait de la deuxième loi de Morgen. Donc, PAS OUI OU PAS NON = PAS (OUI ET NON) :
-> while (strcmp(confirmation, "OUI") != 0 || strcmp(confirmation, "NON") != 0) -> tu répètes la demande
=
tant que PAS OUI OU PAS NON -> tu répètes la demande
=
tant que PAS (OUI ET NON) -> tu répètes la demande.
Donc, la demande sera répétée tant qu'on n'aura pas OUI et NON en même temps ? Encore donc, comme c'est ici impossible d'avoir les deux valeurs en même temps, la demande sera répétée à l'infini, quoi qu'on écrive (OUI, NON, ou n'importe quoi ?) ?
(b)
Et aussi, j'aimerais être sûre d'avoir compris le code :
"oui" étant un booléen, il doit valoir 1 pour être vrai. Or, si strcmp(confirmation, "OUI") est vrai, il aura la valeur de zéro. Il faut donc inverser cette valeur pour qu'elle corresponde à ce qu'on attend du booléen, d'où le !strcmp (idem avec le booléen "non"). C'est bien juste ?
Tout à fait. En utilisant strcmp comme un simple comparateur d'égalité (ce qu'il n'est pas), on pourrait admettre qu'il fonctionne "à l'envers", c'est à dire 0 pour "égal" et une valeur autre pour "inégal". Je suppose qu'il serait plus juste de définir strcmp comme "une mesure d'inégalité".
"Pas oui ou pas non" est en effet la négation de "oui et non". Comme "oui et non" est toujours faux, "pas oui ou pas non" est toujours vrai, et la boucle est du coup infinie.
Robun : je t'assure, tu ne m'as pas du tout embrouillée (les chaînes de caractère fonctionnent bien, dans ce cas, comme les concepts OUI et NON. J'ai super bien choisi mon exercice, en fait).
Bon ben, merci à tous, j'ai enfin compris. Même si je sens que je vais encore me faire avoir, au moins, je saurai pourquoi...
Un petit conseille quand on commence : il ne faut pas hésiter à s’éclaircir les idées en ajoutant des variables pour "découper" les calculs. Même encore, je le fais régulièrement quand mes équations commencent à prendre une certaine ampleur :
void choixPseudo(void)
{
char pseudo[20+1],
confirmation[3+1]= {0}; //Réponse attendue : OUI ou NON -> 3 lettres + 1 caractère de fin de chaîne.
puts ("Choisissez votre pseudo (max. 20 caractères : )");
fgets (pseudo, 21, stdin);
viderBuffer(); //Cette fonction est déclarée par ailleurs et marche normalement.
printf("Votre pseudo est : %s", pseudo);
bool oui = false,
non = false,
reponseValide = false;
while (!oui && !non) // <=> while( !reponseValide) <- plus proche du langage humain
{
puts("Votre pseudo vous convient-il ? Tapez OUI ou NON (majuscules uniquement)\n.");
fgets(confirmation, 4, stdin);
viderBuffer();
oui = !strcmp(confirmation, "OUI");
non = !strcmp(confirmation, "NON");
reponseValide = oui || non;
}
}
Dans l'exemple la variable reponseValide n'est pas utilisée, c'est elle qui devrait être testée dans la boucle. Les variables oui et non ont plutôt un caractère de variables auxiliaires locales.
Pour la comparaison de chaines, le test à 0 me paraît plus lisible
bool oui = strcmp(confirmation, "oui") == 0;
Parce que plus visible que le point d'exclamation. Ca rappelle aussi que strcmp est un comparateur, c'est à dire une fonction qui sert à déterminer un ordre entre des éléments. Un nombre négatif si le premier vient avant le second, positif si il vient après, et nul sinon.
Une variante est strcasecmp, qui compare en ignorant les differences de casse et retourne donc 0 pour des chaines équivalentes, mais pas forcément égales. C'est autre chose qu'un test d'égalité.
Je fais un petit "up" parce que, si j'avais compris comment fonctionnait le théorème de De Morgan (grâce aux explications ci-dessus et à sa démonstration sous forme de tableau), c'est seulement maintenant que j'ai enfin réussi à l'intégrer. Je mets mon raisonnement ci-dessous, au cas où il serait utile à quelqu'un un jour.
Explication pour: truc OU truc = PAS (truc ET truc)
Situation : s'il n'y a pas de voiture à gauche et à droite, on traverse. S'il y a une voiture à gauche ou à droite, on ne traverse pas.
/*
(a)
Si (il y a une voiture à gauche OU s'il y a une voiture à droite)
{
tu ne traverses pas;
}
(b)
Si (il n'y a pas de voiture à gauche ET il n'y a pas de voiture à droite)
{
tu traverses;
}
Ce qui revient à :
Si (il n'y a pas (de voiture à gauche ET de voiture à droite))
{
tu traverses;
}
*/
Explication pour : truc ET truc = PAS (truc OU truc)
Situation : pour qu'une candidature soit valable, il faut un CV et une lettre de motivation. S'il manque l'un des deux, elle n'est pas valable.
/*
(a)
Si (il y a un cv ET il y a une lettre de motivation)
{
la candidature est valable;
}
Si (il n'y a pas de cv OU pas de lettre de motivation)
{
la candidature n'est pas valable;
}
(b)
Ce qui revient à dire :
Si (il n'y a pas de (cv OU lettre de motivation)
{
la candidature n'est pas valable;
}
*/
Déjà, les loi qui relie les opérations et/ou, elle cause de deux variables, truc et machin.
non(truc et machin) = non(truc) ou non(machin)
ou sous une autre forme
truc et machin = non(non(truc) et non(machin))
avec sa copine
non(truc ou machin) = non(truc) et non(machin)
Pour les démontrer, il faut savoir ce qui sert de définition des opérations et/ou/non au départ. En général, on les définit par leurs tables de vérité, et donc la preuve consiste à étudier les 4 cas possibles. Pas à prendre un exemple où on applique sans s'en rendre compte le truc qu'on voudrait justement démontrer.
ATTENTION : il faut se méfier des mots ET et OU dont l'usage en français courant n'est pas exactement le même qu'en logique.
Exemples,
dans "au menu il y a fromage ou dessert", le ou est exclusif.
à partir de "avec 1 € je peux acheter un pain au raisin" et "avec 1 € je peux acheter un pain aux raisins", je ne peux pas conclure "avec 1 € je peux acheter les deux"
C'est pour ça que pour prouver une formule logique, on fait des tables de vérité (ce qui revient à faire ujne étude cas par cas) ou on fait dans le calcul algébrique (non(non(X) = X), mais on ne s'amuse pas à faire des analogies casse-gueule avec "la réalité de tous les jours".
J'ai bien lu tout ce que tu m'as écrit. Je pense qu'en fait je n'ai pas du tout été claire dans ma démarche, donc je vais expliquer tout ça maintenant.
La première chose à dire, je crois, est que je ne tenais absolument pas à faire une quelconque démonstration (je ne me le permettrais pas : la démonstration existante du théorème de De Morgan est aussi claire qu'on peut l'être). Mon objectif est tout autre.
J'ai lu attentivement les liens que drx m'a fournis plus haut. Je comprends désormais le théorème de De Morgan et sa démonstration, ainsi que les tables de vérité (et en bonus, OR et XOR [la nuance existe aussi dans le domaine littéraire, mais elle ne se manifeste pas dans la forme du "ou" et elle reste malheureusement cantonnée aux analyses littéraires ou linguistiques assez poussées]). Je suis même capable, en me basant sur le raisonnement seul, de les refaire par moi-même. Seulement, je n'ai pas DU TOUT un esprit "mathématique". Je veux dire par là que si j'ai besoin d'une table de vérité ou du théorème de De Morgan pour la moindre petite bisbrouille, je serai obligée soit d'aller les rechercher sur Internet, soit de les refaire en long et en large sur un bout de papier ; je suis en effet incapable de les refaire mentalement et d'aller rechercher les informations qui m'intéressent sans support écrit. Tu vas peut-être me répondre que le plus simple serait de simplement mémoriser les deux formules de De Morgan, qui n'ont rien de compliqué ; néanmoins, de nouveau, mon problème n'est pas de les mémoriser - mais bien de savoir à quels cas elles s'appliquent (je pourrais évidemment finir par tomber sur la solution, mais au prix d'un raisonnement très poussif).
Les deux exemples issus de la vie concrète que j'ai donnés là n'ont pour but que de fournir une première approximation pour tous ceux qui, comme moi, ont une logique plutôt "littéraire" que "mathématique". Si tu veux, il ne s'agit juste que de "raccourcis" ou de "balises" qui permettent de se positionner rapidement dans les tables de vérité ou la démonstration du théorème de De Morgan.
Ceci dit, je comprends bien que le rapprochement que j'ai tenté de faire entre la logique "mathématique" et la logique "littéraire" puisse faire bondir... il s'agit de deux logiques différentes et l'une ne peut pas expliquer correctement ce qui relève du domaine de l'autre. J'en ai bien conscience et, je répète, ce n'est pas mon objectif. Il s'agirait plutôt d'un premier contact pour les faire fraterniser afin de pouvoir passer plus facilement de l'une à l'autre (j'ai l'impression que je ne suis pas claire du tout, là).
Avec ces explications, est-ce que mes deux exemples d'approximation te semblent plus acceptables ; ou est-ce que tu avais directement compris ce que je voulais faire, mais tu penses quand même que ma démarche est trop maladroite ?
Ceci dit, avoir employé à chaque fois deux fois "truc" pour des termes de comparaison différents, j'avoue que ce n'était pas bien malin de ma part.
On utilise les tables de vérité au début parce que c'est le moyen habituel de définir les opérateurs logiques. Ça revient à faire du cas par cas.
A partir de là on peut en tirer des propriétés comme non(non(x))=x, ou les lois de de morgan, qu'on utilise de façon algébrique (On combine, on substitué, on simplifie). C'est le mode de raisonnement habituel, en pratique.
Je répète : attention, les connecteurs logiques sont de faux amis pour le langage naturel, il y a des travaux de linguistes la6 dessus. Pire : l'implication, à qui on a toujours tendance à mêler une notion de causalité.
Si il fait beau et que j'ai reçu du courrier, sur le plan de la logique formelle l'implication "J'ai du courrier donc il fait beau" est vraie....
Si les opérateurs ET et OU sont représentés par les symboles du produit (.) et de la somme (+), ce n'est pas un hasard. Cela permet de faire un lien direct avec l'algèbre classique. Si FAUX vaut 0 et VRAI tout autre valeur non nulle alors on s'y retrouve toujours :
1.1 = 1 <-> = vrai
0.1 = 0 <-> = faux
0+1 = 1 <-> = vrai
1+1 = 2 <-> = vrai
Encore plus fort, les factorisations et développements répondent aux même règles que pour produit et somme avec les même priorités :
a.c + a.b = a.(c+b)
Bref, en terme de booléens on pourrait parfaitement remplacer les opérateurs "||" par "+" et "&&" par "*" :
a && b || c = a*b+c
Pour De Morgan et la disjonction / conjonction, il suffit de savoir que couper/unir la barre de complément de la notation classique revient à transformer OU en ET et inversement :
Ce qu'il faut retenir de tout ça, c'est que réfléchir à ce qu'on veut faire à l'aide d'un papier et d'un crayon avant de coder bille en tête reste valable au 21ième siècle.
La convention de C, 0 = faux et le reste = vrai, c'est juste un bricolage foireux qui implique d'avoir des opérations spécifiques && || qu'on ne peut pas simuler avec de l'arithmétique ou des opérations bit à bit.
Il me semble avoir bien précisé "En terme de booléens", le cas -1 n'est pas un booléen valide dans la traditionnelle représentation de vrai et faux par 1 et 0 des éléments de l'équation.
De plus, je ne faisais qu'évoquer ce qu'implique l'algèbre de Boole et comment le manipuler sur le papier comme je le faisais à l'école, pas l'interprétation qu'un langage peut en faire.
En C la valeur -5.23 est vraie, hors ce cas ne se présente pas dans la pratique Booléenne. Le simple fait que
int main(void)
{
bool toto =5;
printf("toto = %d\n", toto);
printf("toto est vrai ? %s\n", (toto==true)?"oui":"non");
printf("toto est faux ? %s\n", (toto==false)?"oui":"non");
return 0;
}
affiche 5 prouve que l'interprétation du booléen est plutôt légère, alors que la valeur attribuée à toto devrait être "vrai" ou 1. Par la suite, toto n'est donc pas non-plus considéré "vrai" alors qu'il n'est pas "faux" pour avoir accepté une attribution hors ensemble, grave défaillance des définitions de stdbool.h
Il est donc nécessaire d'ajouter un moyen de conversion en booléen puisque le cast ne le fait pas correctement en C :
La notation européenne de la porte "OU" n'est pas notée ">=1" par hasard non-plus. Elle signifie bien qu'elle est passante lorsque la somme de ses entrées donne un résultat (dans N) non nul et potentiellement supérieur à 1.
Merci à vous deux pour vos explications, je n'imaginais pas qu'on puisse dire autant de choses à propos des opérateurs de comparaison. Il ne me reste plus qu'à potasser tout ça.
Question sur les opérateurs de comparaison
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Bonhomme !! | Jeu de plateforme : Prototype.
Bonhomme !! | Jeu de plateforme : Prototype.
Bonhomme !! | Jeu de plateforme : Prototype.
Bonhomme !! | Jeu de plateforme : Prototype.
Bonhomme !! | Jeu de plateforme : Prototype.