Première · Langages et programmation

Spécifier une fonction : préconditions, postconditions et assertions

« La fonction cherche une valeur » laisse de nombreuses questions ouvertes : dans quelles données, avec quel résultat si elle ne trouve rien, et lequel si plusieurs résultats conviennent ? Spécifier consiste à lever ces ambiguïtés avant de programmer.

SofienAvec SofienIngénieur et enseignant en informatique
Dans ce chapitre

Un cap pour ce chapitre

Ce que vous saurez faire

  • Formuler les préconditions et postconditions d’une fonction
  • Choisir un résultat cohérent dans les cas limites
  • Utiliser une assertion sans la confondre avec une preuve
Les bases utiles pour commencer

Décrire un service vérifiable

Une spécification donne le nom de la fonction, ses paramètres, les entrées autorisées et le résultat attendu. Pour moyenne(notes), annoncer seulement « calcule une moyenne » est insuffisant : notes peut-il être vide ? Les éléments sont-ils des nombres ? La fonction renvoie-t-elle un nombre ou affiche-t-elle un message ? Une description courte mais précise facilite l’écriture et les tests. Elle permet aussi de remplacer l’implémentation sans changer les programmes qui utilisent la fonction, tant que le service promis reste identique.

La spécification parle à deux lecteurs : celui qui écrit la fonction et celui qui l’utilise. Le premier doit garantir le résultat pour toutes les entrées autorisées ; le second doit fournir ces entrées et interpréter correctement le résultat obtenu.

Les préconditions délimitent les entrées

Une précondition est une hypothèse que l’appelant doit respecter. Pour moyenne, on peut exiger un tableau non vide de nombres. Pour une recherche dichotomique, on exige notamment un tableau trié dans l’ordre annoncé. En dehors de ces hypothèses, le contrat ne promet pas le résultat habituel. Il est donc important de distinguer une entrée valide difficile, comme un tableau d’un seul élément, d’une entrée interdite, comme un tableau vide pour cette version de moyenne. Ajouter trop de préconditions peut toutefois rendre la fonction inutilement restrictive.

Une précondition ne doit pas être ajoutée après coup uniquement pour masquer un défaut. Si un maximum doit fonctionner sur des nombres quelconques, interdire les nombres négatifs rendrait le contrat artificiellement faible. Initialiser avec un élément du tableau respecte davantage le service attendu.

Les postconditions disent ce qui doit être vrai à la sortie

Une postcondition caractérise le résultat pour toute entrée respectant les préconditions. Pour maximum, « renvoyer un élément du tableau supérieur ou égal à tous les autres » est plus précis que « renvoyer une grande valeur ». Pour chercher un indice, on peut imposer le premier indice correspondant, ou autoriser n’importe lequel : ces contrats sont différents. Préciser aussi si le tableau d’entrée doit rester inchangé évite des surprises. Une bonne postcondition peut servir à construire un test, mais elle n’a pas besoin de raconter l’algorithme utilisé.

Une postcondition peut décrire plusieurs propriétés complémentaires. Pour un tri, le résultat doit être ordonné et conserver les éléments, y compris leurs répétitions. Vérifier uniquement l’ordre accepterait le tableau vide pour toutes les entrées, ce qui ne constitue pas un tri.

Exprimer des contrôles avec assert

L’instruction assert condition lève AssertionError si la condition est fausse. Elle peut rendre une hypothèse explicite pendant la mise au point, par exemple assert len(notes) > 0. Elle ne répare pas une entrée et ne garantit pas toutes les propriétés du programme. Les assertions Python peuvent être désactivées avec un mode d’exécution optimisé : elles ne remplacent donc pas la validation indispensable d’une saisie dans une application. En Première, leur intérêt principal est de relier un contrat, une condition testable et un diagnostic clair.

def milieu(a, b):
    assert a <= b
    resultat = (a + b) / 2
    assert a <= resultat <= b
    return resultat

Une assertion vraie reste silencieuse : cela signifie seulement que sa condition est satisfaite à cet endroit. Pour diagnostiquer un échec, lisez les valeurs concernées et demandez si l’erreur vient de l’appelant, d’un calcul interne ou d’une propriété mal formulée.

Spécifier une recherche avec un cas d’absence

On définit premier_indice(t, cible) sur tout tableau, y compris vide. Le résultat doit être le plus petit indice contenant la cible ; si elle est absente, la fonction renvoie -1. Pour [6, 2, 6] et la cible six, le résultat attendu est zéro ; pour la cible cinq, il est moins un. Le tableau doit rester inchangé.

Ces phrases suffisent à construire des tests distinguant une occurrence quelconque de la première. Elles n’imposent pas la manière de parcourir le tableau. Le cas d’absence fait partie du résultat normal ; il ne constitue pas une violation des préconditions. Le programme appelant doit le reconnaître avant d’utiliser le résultat comme indice.

Un contrat doit rejeter les réponses artificielles

Pour le minimum de [8, 3, 5], exiger seulement un résultat inférieur ou égal à chaque élément accepte zéro ou moins cent. Exiger seulement l’appartenance accepte huit. Il faut les deux propriétés ensemble : appartenir au tableau et ne dépasser aucun de ses éléments. Trois est alors la seule réponse possible.

De même, pour une division euclidienne d’entiers naturels par un entier strictement positif, une paire quotient-reste doit reconstruire le dividende et donner un reste compris entre zéro inclus et le diviseur exclu. L’égalité seule accepte plusieurs paires. Une caractérisation complète évite qu’un programme incorrect passe un contrôle trop faible.

À vous de faire varier les choses

Ce résultat respecte-t-il vraiment le contrat ?

Choisissez un tableau et une réponse proposée pour maximum. Examinez séparément les deux propriétés nécessaires.

Lire le résultat de l’expérience initiale

Postcondition non respectée

Être supérieur aux éléments ne suffit pas : le résultat doit aussi appartenir au tableau.

PropriétéRésultat
Tableau non videtrue
Résultat présentfalse
Résultat supérieur ou égal à chaque élémenttrue

Un contrat rassemble toutes les propriétés utiles. En oublier une peut laisser passer une réponse manifestement incorrecte.

De la compréhension à l’autonomie

À vous de résoudre

Cherchez d’abord par vous-même. Vérifiez les résultats demandés, utilisez les indices si nécessaire, puis comparez votre méthode à la correction.

Exercice 1 · S’entraîner#

Une division autorisée

Spécifiez quotient(a, b), qui renvoie a / b pour deux nombres. Quelle précondition ne faut-il pas oublier ?

Indice 1

Demandez-vous quelle valeur rend la division impossible.

Indice 2

Le résultat peut être un flottant même pour deux arguments entiers.

Comprendre la correction

On précise que a et b sont des nombres, que b doit être différent de zéro et que le résultat renvoyé est le quotient a / b. Le contrat n’annonce pas un quotient entier. Une assertion b != 0 peut rendre l’hypothèse visible pendant la mise au point, sans se substituer à une validation d’entrée.

Exercice 2 · S’entraîner#

Le premier ou un indice

Dans [8, 3, 8], une recherche de 8 renvoie 2. Est-ce correct ? Comparez deux contrats possibles.

Indice 1

La valeur apparaît à deux positions.

Indice 2

Le contrat peut imposer le plus petit indice ou accepter n’importe quelle occurrence.

Comprendre la correction

Le résultat 2 est correct si la fonction promet un indice portant la valeur cherchée. Il est incorrect si elle promet le premier indice, qui vaut 0. On ne peut donc juger l’algorithme à partir du nom seul. La précision de la postcondition détermine ce qui doit être vérifié.

Exercice 3 · S’entraîner#

Un faux maximum

Un programme renvoie 100 pour le tableau [4, 7, 2]. Il vérifie que son résultat est supérieur à tous les éléments. Pourquoi ce test ne suffit-il pas ?

Indice 1

Un maximum doit appartenir aux données.

Indice 2

Deux propriétés sont nécessaires, pas une seule.

Comprendre la correction

100 est supérieur à tous les éléments mais n’est pas un élément du tableau. La postcondition doit exiger à la fois l’appartenance du résultat au tableau et sa supériorité ou égalité à chaque valeur. Le maximum correct est 7. Cette double caractérisation écarte les grandes valeurs artificielles.

Exercice 4 · S’entraîner#

Une assertion qui réussit

Un test assert carre(3) == 9 passe. Peut-on conclure que carre est correcte pour tous les entiers ? Proposez un autre test utile.

Indice 1

Un résultat observé concerne une seule entrée.

Indice 2

Une fonction qui renvoie toujours 9 réussirait aussi ce test.

Comprendre la correction

Non : le test confirme uniquement le cas 3. On peut ajouter carre(0) == 0 et carre(-2) == 4 pour examiner zéro et les nombres négatifs. Même plusieurs succès ne constituent pas une preuve générale. Il faut relier le calcul effectué à la postcondition pour justifier tous les entiers autorisés.

Exercice 5 · Approfondir et relier#

Le minimum trop petit

Pour [8, 3, 5], une fonction propose zéro comme minimum. Formulez les deux propriétés nécessaires, identifiez celle qui échoue, puis donnez un candidat qui appartient au tableau mais échoue à l’autre propriété.

Indice 1

L’appartenance et la comparaison jouent des rôles différents.

Indice 2

Huit est présent, mais il existe plus petit.

Comprendre la correction

Zéro est inférieur à toutes les données mais n’appartient pas au tableau. Le candidat huit appartient au tableau mais dépasse trois et cinq. Le minimum doit être présent et inférieur ou égal à chaque valeur ; trois satisfait les deux propriétés. Aucun tri préalable n’est nécessaire pour définir ce contrat.

Exercice 6 · Approfondir et relier#

Reconstruire ne suffit pas

Pour diviser 23 par 5, deux réponses proposées sont quotient 4 reste 3, puis quotient 3 reste 8. Vérifiez la reconstruction dans les deux cas. Ajoutez une contrainte permettant de choisir la réponse correcte.

Indice 1

Le reste ne doit plus permettre de former un groupe complet.

Indice 2

Il doit être positif ou nul et strictement inférieur à cinq.

Comprendre la correction

Les deux paires reconstruisent 23 : vingt plus trois et quinze plus huit. Seule la première donne un reste dans l’intervalle autorisé. Avec un reste huit, on pourrait encore former un groupe de cinq. Le contrat exige donc reconstruction et encadrement du reste, en plus des types entiers.

Exercice 7 · Approfondir et relier#

Un indice d’absence dangereux

Une recherche promet le premier indice ou -1 si la cible manque. Sur [7, 7, 4], donnez les réponses pour les cibles 7 et 9. Un appelant lit directement t[resultat]. Expliquez le danger quand la cible est absente et proposez le test préalable.

Indice 1

En Python, moins un désigne aussi le dernier élément.

Indice 2

Une valeur spéciale doit être interprétée avant l’accès.

Comprendre la correction

Les résultats sont zéro pour sept et moins un pour neuf. Dans le second cas, t[-1] lirait quatre : l’absence serait masquée par un accès valide mais sans rapport avec la recherche. L’appelant doit d’abord vérifier resultat != -1, puis seulement lire la case lorsque la cible a été trouvée.

Les erreurs qui méritent un détour

Utiliser le contrat pour décrire seulement les étapes
Décrivez d’abord ce qui est garanti, indépendamment de la manière de l’obtenir.
Traiter assert comme une correction automatique
Une assertion signale un écart. Elle ne transforme pas les données et ne prouve pas les cas non exécutés.

La fiche à garder

L’essentiel à retenir

  • Les préconditions engagent l’appelant.
  • Les postconditions caractérisent le résultat promis.
  • Une assertion vérifie une propriété lors d’une exécution donnée.

Cette notion au bac

Retrouvez ces idées dans un sujet complet, avec des indices, une correction expliquée et des ateliers.

Le prochain pas

Retrouver le catalogue de Première

Ce chapitre s’appuie sur le programme officiel de Première (PDF, nouvel onglet). Les explications et exercices sont proposés pour l’apprentissage.