====== Correction ======
===== Définitions =====
On se demande si un algorithme -- ou, cela revient au même, une fonction -- est correct. C'est à dire que l'on veut savoir si l'algorithme fait bien ce qu'il est censé faire et dans quelles conditions il fonctionne correctement.
**Précondition**
Il s'agit d'un condition portant sur les entrées. Pour que l'algorithme fonctionne correctement, il faut que les préconditions soient respectées.
**Postcondition**
Ce sont les conditions que nous voulons voir réalisées à la fin de l'exécution de l'algorithme. C'est en vérifiant que les postconditions sont respectées que l'on pourra juger que l'algorithme a fait ce qu'il devait.
**Terminaison**
On veut que l'algorithme se termine, c'est à dire que son temps d'exécution soit fini. En pratique, on a besoin que ce temps ne soit pas trop long. En théorie, il suffit que ce temps soit fini, même s'il est très grand. Un algorithme **termine** si son **temps** d'exécution est **fini** chaque fois que les préconditions sont vérifiées.
**Correction**
On dira qu'un algorithme (ou fonction) est **correct** s'il **termine** ET que préconditions $\Rightarrow$ postconditions.
===== Exemple =====
Je considère une fonction ''solve(A:int) -> int'' qui renvoie le plus petit entier ''N'' tel que ''%%N**3 >= A%%''.
def solve(A:int) -> int:
'''
renvoie le plus petit entier N tel que N**3 >= A
'''
n = 0
while n**3 < A:
n += 1
return n
=== Terminaison ===
Cette fonction contient une boucle ''while''. Il faut regarder ces boucles avec soin car elles sont potentiellement illimités.
Ici, ''n'' augmente de 1 à chaque boucle, donc ''%%n**3%%'' augmente d'au moins 1 à chaque boucle, et donc ''%%n**3%%'' finira par dépasser ''A''.
La fonction termine donc.
=== Correction ===
On a déjà vérifié que la fonction termine.
Fait-elle ce qu'elle doit ?
**Non !** En effet, si ''A = -10'', la fonction renvoie ''0'' alors que la réponse aurait dû être ''-2''.
La fonction a été écrite en supposant **implicitement** que ''A'' serait positif. Si ''A >= 0'', on n'a plus de problème. Il faut faire attention à ces implicites. Les préconditions sont là justement pour expliciter.
On ajoute donc la précondition : ''A >= 0''. Réécrivons la fonction :
def solve(A:int) -> int:
'''
A entier positif
renvoie le plus petit entier N tel que N**3 >= A
'''
n = 0
while n**3 < A:
n += 1
return n
La précondition a été écrite dans le commentaire. Dans certains cas, cela peut suffire. Mais rien n'empêche ici de demander :
>>> solve(-10)
0
La précondition écrite dans le commentaire n'est qu'informative et n'interdit pas un mauvais usage de la fonction.
On peut être plus contraignant :
def solve(A:int) -> int:
'''
A entier positif
renvoie le plus petit entier N tel que N**3 >= A
'''
assert A >= 0
n = 0
while n**3 < A:
n += 1
return n
''assert'' énonce une condition qui doit être valide. Si elle ne l'est pas, l'exécution s'interrompt sur une erreur.
Avec la précondition ''A >= 0'', la fonction est correcte.
La commande ''assert'' a d'autres usages :
* pour les tests : vérifier les fonctions sur des cas connus d'avance pour s'assurer qu'elles fournissent les bons résultats,
* pour la maintenance : certains environnement de programmation comme [[https://code.visualstudio.com/|VSCode]] sont capable de détecter les ''assert'' et de s'en servir pour vérifier la cohérence de l'ensemble. Ainsi, si on a affirmé par ''assert'' que telle variable devait être entière mais qu'ailleurs on écrit un ''float'' dans cette variable, l'environnement nous préviendra de l'incohérence.