Python - correction exercices Conditions
Attention, en Python, du fait de la nature très "tolérante" de ce langage, on a (trop) vite fait de coder "comme on parle" ( plutôt comme un anglophone parle 😉 ); c'est particulièrement vrai pour les conditions.
Ainsi, pour tester si une variable est comprise entre deux valeurs, on peut effectivement écrire :
if 18.5 < imc <= 25:
...
Et ça marche ! ( du moins, avec un Python récent...) !
Cependant, c'est une écriture uniquement "pythonesque", et pas du tout comprise par d'autres langages, pour lesquels on doit séparer l'évaluation de condition en deux tests liés :
if imc > 18.5 and imc <= 25:
...
Même en Python, vous éviterez dorénavant ce genre de raccourci propre au langage...
Par contre, il y a d'autres écritures qui, si elles ne provoquent pas d'erreur, ne font absolument pas ce que l'on veut; par exemple, pour tester si une variable est égale à une valeur OU à une autre, on aurait vite tendance à écrire des choses comme :
if jour == "lundi" or "jeudi":
...
ça ne provoque pas d'erreur, car "jeudi" est vu dans une instruction if comme une expression booléenne qui est toujours évaluée à True;
le test ci-dessus est donc toujours vrai ( rappelez-vous la table de vérité du or )! ...
Il faut donc impérativement, dans une situation pareille, "dédoubler" le test :
if jour == "lundi" or jour == "jeudi":
...
La plus petite des deux
Proposition de script
def plus_petite(n1, n2):
if n1 < n2:
pp = n1 # on utilise une variable 'pp' qui contiendra le résultat à renvoyer
elif n2 < n1:
pp = n2
else:
pp = n1, n2 # si les deux valeurs sont identiques, on les renvoie toutes les deux !
return pp
print("La valeur la plus petite est", plus_petite(15, 23))
Il est à noter que l'on peut très bien se passer de la variable pp, et utiliser à la place directement plusieurs instructions return à différents endroits de la fonction :
def plus_petite(n1, n2):
if n1 < n2:
return n1
elif n2 < n1:
return n2
else:
return n1, n2
print("La valeur la plus petite est", plus_petite(15, 23))
Remarques
Il y a bien 3 cas à évaluer ici : pensez au cas où les deux valeurs sont égales !
La plus longue chaîne
Proposition de script
Avec la même "astuce" que ci-dessus :
def plus_longue(chaine1, chaine2):
l1 = len(chaine1) # on détermine tout d'abord le nombre de caractères de chacune des chaînes
l2 = len(chaine2)
if l1 < l2: # on fait ensuite les tests sur leur longueur
return "La plus longue chaîne est " + chaine2 + " avec " + l1 + " caractères."
elif l2 < l1:
return "La plus longue chaîne est " + chaine1 + avec " + l2 + " caractères."
else:
return "Les deux chaînes sont de même longueur : " + l1 + " caractères."
print(plus_longue("ababab", "bababababab"))
Remarques
Ici, on construit directement une chaîne à renvoyer, formée de la concaténation de deux autres chaînes; il n'y a alors qu'à afficher directement le résultat renvoyé par la fonction.
IMC
Proposition de script
def calcul_imc(taille, poids):
imc = poids / (taille**2) # calcul de la valeur de l'imc
if (imc < 18.5):
return imc, "poids insuffisant."
elif (imc >= 18.5 and imc < 25):
return imc, "poids normal."
elif ( imc >= 25 and imc < 30):
return imc, "surpoids."
else :
return imc, "obésité."
t = 1.75
p = 75
res1, res2 = calcul_imc(t, p) # appel de la fonction, et stockage de ses résultats renvoyés dans deux variables : imc -> res1, chaine -> res2
print("Votre IMC est", res1)
print("Vous êtes en", res2)
Remarques :
- attention, quand on évalue deux conditions sur la même variable ( c'est le cas ici de la variable
imclorsqu’on cherche à savoir si sa valeur est dans un certain intervalle, par exemple ligne 7 ), il est nécessaire de répéter pour chaque évaluation le nom de la variable.
On n'aurait par exemple pas pu écrire :elif (imc >= 18.5 and < 25): - Pour la dernière évaluation, à la place du
else( ligne 11 ), on pouvait "insister" pour être certain sur la condition à évaluer :
...mais cela n'est pas nécessaire, car tous les autres cas ont été évalués précédemment, il s'agit donc bien ici d'un SINON.elif (imc >= 30): return imc, "Obésité." - on renvoie ici deux résultats : la valeur de l'IMC, et une chaîne de caractères qui indique le diagnostic sur le poids.
- On a bien pris soin de donner des noms différents aux variables qui contiennent les données de taille et de poids dans la fonction et dans le programme principal; de même pour les deux résultats renvoyés par la fonction ( imc et la chaîne ) et "récupérés" par le programme principal ( res1 et res2 ).
- On pouvait penser que le seul rôle de la fonction serait de calculer l'IMC, les conditions étant alors évaluées dans le programme principal.
Quel est l'intérêt d'avoir comme ici une fonction qui fait "tout" ? Si la fonction doit être utilisée à plusieurs endroits d'un programme, et que l'on souhaite à un moment ou un autre changer le texte qui s'affiche pour le diagnostic sur l'IMC, alors il faudra changer ce texte partout dans le code, ce qui peut être fastidieux... alors qu'ici, il n'y a qu'à modifier le texte uniquement dans la fonction pour que les modifications prennent effet partout dans le code : la modification et le débogage du code sont bien plus faciles en procédant ainsi !
Combien de caractères...
Proposition de script
def compte_caractere(txt, car):
compteur = 0 # variable auxiliaire servant de compteur de caractères
for lettre in txt : # pour chaque lettre du texte :
if lettre == car : # si la lettre courante est identique au caractère recherché,
compteur = compteur + 1 # alors on incrémente le compteur
return compteur
long_texte = "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatu"
caractere = 'e'
res = compte_caractere(long_texte, caractere) # appel de la fonction, et stockage de son résultat renvoyé
print("Le caractère", caractere, "se trouve", res, "fois dans le texte.")
Remarques
- on rappelle comment fonctionne la boucle
for car in texte: elle parcourt la chaînetextecaractère après caractère, et stocke à chaque tour le caractère courant dans la variablecar. - bien observer l'indentation dans cette boucle : l'instruction
if( ligne 6 ) doit être exécutée à chaque tour de boucle ( donc pour chaque caractère ), elle est donc en retrait par rapport au mot-cléfor; de même, l'incrémentation du compteur ( ligne 7 ) ne doit être exécutée que si la condition évaluée par leifest vraie, elle est donc indentée par rapport auif.
Si l'on avait écrit :
...le compteur aurait été incrémenté à chaque tour de la boucle : cette variable contiendrait alors le nombre total de caractère du texte à la fin du parcours de la chaîne !for lettre in txt : if (lettre == car) : ... compteur = compteur + 1
Année bissextile
Proposition de script
def est_bissextiel(annee):
if ( annee % 4 == 0 and annee % 100 !=0 or annee % 400 == 0 ) :
return True
else :
return False
print(est_bissextile(2022))
Remarques
- On pouvait également séparer les deux conditions :
if ( annee % 4 == 0 and annee % 100 !=0) : return True elif ( annee % 400 == 0 ) : return True else : return FalseVoire même, "imbriquer" des instructions
if:if annee % 4 == 0: if annee % 100 !=0 : return True if annee % 400 == 0 : return True return FalseEn fait, il y a généralement plusieurs façons d'écrire un code qui fasse la même chose, à condition de respecter la logique de ce que l'on veut faire.
La différence entre ces différentes façons est une question d'optimisation, c'est à dire écrire judicieusement un code en utilisant toutes les possibilités du langage. - en parlant d'optimisation, on peut remarquer quelque chose d'intéressant : après une instruction
return, on peut placer toute expression, c'est à dire une combinaison de symboles qui est égale à un certain résultat; ce peut être une simple valeur, une variable, ou alors un calcul, etc..
Or, une évaluation de condition est aussi une expression : on peut donc directement renvoyer son résultat, sans même utiliser d'instructionif:def est_bissextile_optim(annee): return ( annee % 4 == 0 and annee % 100 !=0 or annee % 400 == 0 ) # on renvoie directement le résultat de l'évaluation de la condition ! print(est_bissextile(2022))Et oui, ça marche très bien ! L'évaluation d'une condition est une expression donnant
TrueouFalsecomme résultat, aucun problème donc à l'utiliser avec unreturn!Pensez à cela dès qu'une de vos fonctions doit renvoyer le résultat de n'importe quelle expression, on peut très souvent éviter d'utiliser des variables intermédiaires.
Comme l'a dit le grand théoricien de l'algorithmique Donald Knuth :
"Premature optimization is the root of all evil (or at least most of it) in programming." ( L'optimisation prématurée est la racine de tous les maux (ou, du moins, la plupart d'entre eux) en programmation. )
Suivez ce conseil, et écrivez d'abord votre code en réfléchissant à la logique de ce que vous voulez faire, même si cela ne vous paraît pas très "élégant". Ensuite, éventuellement, et seulement si vous maîtrisez cela, vous pourrez réfléchir à l'optimisation de votre code afin d'améliorer sa clarté.
Quel jour était-il le... ?
Comme indiqué dans la consigne, on va donc réutiliser la fonction précédente en l'appelant à l'étape concernée de l'algorithme :
Proposition de script
def est_bissextile(annee):
# code repris de l'exercice précédent
return ( annee % 4 == 0 and annee % 100 !=0 or annee % 400 == 0 )
def jour_date(siecle, an, jour, mois, annee):
#application de la formule de l'étape 3 au calcul de la variable 'valeur'
valeur = an + an // 4
# appel de la fonction est_bissextile, et stockage de son résultat dans une variable 'bissext'
bissext = est_bissextile(annee)
# test sur le siècle
if siecle == 18 :
valeur = valeur + 2
elif siecle == 20 :
valeur = valeur + 6
#tests sur le mois
if (mois == 1) and (bissext == False ) : # mois = janvier et année pas bissextile ?
valeur = valeur + 1
if (mois == 2): # mois = février ?
if bissext == True: # Deuxième test imbriqué dans le précédent
valeur = valeur + 3
else :# sous-entendu : si bissext == Faux...
valeur = valeur + 4
if (mois == 3) or (mois == 11) :
valeur = valeur + 4
if mois == 5 :
valeur = valeur + 2
if mois == 6 :
valeur = valeur + 5
if mois == 8 :
valeur = valeur + 3
if mois == 10 :
valeur = valeur + 1
if (mois == 9) or (mois == 12 ):
valeur = valeur + 6
valeur = ( valeur + jour ) % 7
#détermination du jour en fonction de la valeur de la variable 'valeur'
if valeur == 1 :
jr ='dimanche'
if valeur == 2 :
jr ='lundi'
if valeur == 3 :
jr ='mardi'
if valeur == 4 :
jr ='mercredi'
if valeur == 5 :
jr ='jeudi'
if valeur == 6 :
jr ='vendredi'
if valeur == 0 :
jr ='samedi'
# renvoi du résultat
return jr
print(jour_date(20, 22, 1, 10, 2022)) # appel de la fonction pour la date du 01/10/2022
Remarques
- Le test ( complexe ! ) sur le mois de février pouvait aussi être écrit sous la forme suivante :
if (mois == 2) and (bissext == True) : valeur+=3 elif (mois == 2) and (bissext == False) : valeur+=4Il est à noter que bissext contient déja une expression booléenne, on peut donc en fait simplement écrire :
if (mois == 2) and bissext : valeur+=3 elif (mois == 2) and not bissext : valeur+=4 l'appel de la fonction demande de lui passer 5 arguments ! C'est beaucoup...
On pourrait simplifier le code en constatant que les deux premiers arguments peuvent être calculés dans la fonction à partir de la valeur de annee :
siecle = annee // 100 # par exemple : 20 = 2025 // 100 an = annee % 100 # par exemple : 25 = 2025 % 100La fonction n'a alors besoin que de 3 paramètres : jour, mois et annee.
- Il existe d'autres méthodes de calcul du jour correspondant à une date donnée, voir par exemple : Cursiosités mathématiques.