Ce cours/TP a pour objectif de vous faire chercher des erreurs dans des algorithmes, écrits en Python.

Pour tester vos programmes, utilisez votre environnement de développement ou utlisez le site http://pythontutor.com/ ou https://repl.it/languages/Python3

1. Introduction

La programmation est une démarche très complexe, et comme c’est le cas dans toute activité humaine, on y commet de nombreuses erreurs. Pour des raisons anecdotiques, les erreurs de programmation s’appellent des « bugs » (ou « bogues », en Français), et l’ensemble des techniques que l’on met en œuvre pour les détecter et les corriger s’appelle « debug » (ou « débogage »).

Il peut exister dans un programme trois types d’erreurs assez différentes, et il convient de savoir bien les distinguer.

1.1. Erreurs de syntaxe

Tout langage comporte sa syntaxe. Dans la langue française, par exemple, une phrase doit toujours commencer par une majuscule et se terminer par un point.

ainsi cette phrase comporte deux erreurs de syntaxe

Dans les textes ordinaires, la présence de quelques petites fautes de syntaxe par-ci par-là n’a généralement pas d’importance. Cela n’empêche pas que l’on puisse comprendre le texte.

Dans un programme d’ordinateur, par contre, la moindre erreur de syntaxe produit invariablement un arrêt de fonctionnement (plantage) ainsi que l’affichage d’un message d’erreur.

Gardez à l’esprit que les mots et les symboles utilisés n’ont aucune signification en eux-mêmes : ce ne sont que des suites de codes destinés à être convertis automatiquement en nombres binaires. Par conséquent, il vous faudra être très attentifs à respecter scrupuleusement la syntaxe du langage.

Tous les détails ont de l’importance.

  • Attention à la casse (c’est-à-dire l’emploi des majuscules et des minuscules)

  • Attention à la ponctuation. Toute erreur à ce niveau (même minime en apparence, tel l’oubli d’une virgule, par exemple) peut modifier considérablement la signification du code, et donc le déroulement du programme.

  • Attention aux indentations. Elles définissent les limites des blocs de codes en particulier dans les tests, dans les boucles et dans les fonctions.

1.2. Erreurs sémantiques

Le second type d’erreur est l’erreur sémantique ou erreur de logique. S’il existe une erreur de ce type dans un de vos programmes, celui-ci s’exécute parfaitement, en ce sens que vous n’obtenez aucun message d’erreur, mais le résultat n’est pas celui que vous attendiez : vous obtenez autre chose.

En réalité, le programme fait exactement ce que vous lui avez dit de faire. Le problème est que ce que vous lui avez dit de faire ne correspond pas à ce que vous vouliez qu’il fasse. La séquence d’instructions de votre programme ne correspond pas à l’objectif poursuivi. La sémantique (la logique) est incorrecte.

Rechercher des fautes de logique peut être une tâche ardue. C’est là que se révélera votre aptitude à démonter toute forme résiduelle de « pensée magique » dans vos raisonnements. Il vous faudra analyser patiemment ce qui sort de la machine et tâcher de vous représenter une par une les opérations qu’elle a effectuées, à la suite de chaque instruction.

1.3. Erreurs a l’execution

Le troisième type d’erreur est l’erreur en cours d’exécution (Run-time error), qui apparaît seulement lorsque votre programme fonctionne déjà, mais que des circonstances particulières se présentent (par exemple, votre programme essaie de lire un fichier qui n’existe plus).

Ces erreurs sont également appelées des exceptions, parce qu’elles indiquent en général que quelque chose d’exceptionnel (et de malencontreux) s’est produit.

2. Trouver les erreurs

2.1. Travail faire : Jouer avec les variables

Chacun des programmes suivants admet une erreur : trouvez-la de tête avant de vérifier votre hypothèse sous Python. Identifiez la nature de l’erreur.

Code de la fonction 1
def fonction1(n : int) -> int:
  a = 1
  while a < n :
    b = 2*b + 1
    a = a+1
  return b
Code de la fonction 2
def fonction2(n : int) -> int:
  b = 1
  while n != 0 :
    b = 2*b
    n = n-2
  return b
Code de la fonction 3
def fonction3(n : int) -> int:
  a = 1
  b = 0
  while a < n :
    b = b + a
  return b
Code de la fonction 4
def fonction4(a : int, n : int) -> int:
  b=1
  c=a
  while b < n :
    c = c x a
    b = b + 1
  return c

2.2. Travail faire : La fonction Factorielle

En mathématiques, la fonction factorielle est définie par la suite :

\$n! = 1 \times 2 \times 3 \times \ldots \times (n-1) \ times n\$

Parmi les algorithmes suivants, lesquels sont corrects ? Après avoir donné une réponse de tête, évaluer les programmes sous Python à l’aide d’une batterie de tests bien choisis : un programme sera alors déclaré incorrect dès lors qu’au moins un des tests est falsifié, et probablement correct dès lors que tous les tests sont validés.

Ce n’est pas parsqu’un programme fonctionne correctement avec les données avec lesquelles ont l’a testé qu’il fonctionnera dans tous les cas.

On ne généralise pas à partir d’un cas particulier !

Pour affirmer qu’un algorithmes est correct, vous devez fournir un argument, par exemple sous la forme d’un invariant de boucle.

def factorielle1(n : int) -> int:
  i = 0
  f = 1
  while i <= n :
    i = i+1
    f = f*i
  return f
def factorielle2(n : int) -> int:
  i = 0
  f = 1
  while i < n :
    i = i+1
    f = f*i
  return f
def factorielle3(n : int) -> int:
  f = 1
  for i in range(1,n+1):
    f = f*i
  return f
def factorielle4(n : int) -> int:
  f = 1
  for i in range(n):
  f = f*i
return f

3. Identification du traitement réalisé par un programme

Il est parfois difficile à la seule lecture d’un programme de comprendre ce qu’il fait exactement. Le développeur doit donc prendre un soin tout particulier à bien nommer ces fonctions et à commenter son code.

3.1. Travail faire : Que fait ce programme ?

Un élève a produit de drôles de programmes. Saurez-vous trouver ce qu’ils renvoient ? Utiliser des tests en Python pour se donner une idée, puis essayez de trouver des arguments pour valider vos conjectures.

Donnez un nom cohérent à ces fonctions et commentez-les.

def mystere1(n : int, m : int) -> int:
''' Indiquez ici ce que fait la fonction, ce quelle attend en paramètres et ce quelle retourne '''
  i = m
  while i < n :
    i = i+1
  return i

def mystere2(n : int) -> int:
''' Indiquez ici ce que fait la fonction, ce quelle attend en paramètres et ce quelle retourne '''
  a = 0
  s = 1
  t = 1
  while s <= n :
    a += 1
    s += t+2
    t += 2
  return a

4. Le bug de Zune

s’agit d’un bug rencontré par les lecteursMP3 Microsoft Zune, le 31 décembre 2008 : le matin de ce jour-là, les utilisateurs voulant allumer leur appareil ont eu la désagréable surprise de découvrir l’écran de démarrage restant gelé comme dans l’image ci-dessous.

zune

Impossible pour eux d’utiliser leur lecteur. Le plus drôle dans l’histoire est que rien ne pouvait être fait pour régler ce problème, mais que le lendemain matin, le 1er janvier 2009, tout s’est remis à fonctionner comme si rien ne s’était jamais passé. Une mise à jour du logiciel a évidemment ensuite été mise en ligne pour corriger le bug. Mais quel est donc ce bug étrange ? Comme on peut le deviner du fait du redémarrage sans souci le lendemain, le bug se situe dans la partie du code en charge des fonctions calendaires du lecteur. Microsoft a rapidement trouvé l’origine du bug qu’ils ont depuis publié : la faute provient de 10 lignes de code écrites originellement en C, traduite ici en Python :

year = 1980
while days > 365:
  if is_leap_year(year):
    if days > 366:
      days -= 366
      year += 1
  else:
    days -= 365
    year += 1

Ces quelques lignes utilisent une variable globale days qui est initialisée, lors du démarrage du lecteur, au nombre de jours écoulés depuis le 1er janvier 1980

Contrairement aux systèmes Unix/linux qui utilisent la date du 1er janvier 1970 comme epoch, c’est-à-dire comme date initiale à partir de laquelle est mesuré le temps par un système d’exploitation Unix/linux, pour Microsoft, le début des temps est le 1er janvier 1980.

Le principe du code est assez simple, et utilise un fonction booléenne is_leap_year qui renvoie True si l’année est bissextile et False sinon.

4.1. Travail faire : Compléter le code

  • Commencez par écrire le code de la fonction is_leap_year en se rappelant qu’un numéro d’année est bissextile si elle est divisible par 4 sans être divisible par 100, ou si elle est divisible par 400.

  • Testez le code du Zune, en attribuant préalablement une valeur à la variable days.

4.2. Travail faire : identifier le bug

  • Trouvez une valeur de days où le programme bugge. Décrivez le comportement du programme dans ce cas et expliquez le comportement observé du lecteur Zune le matin du 31 décembre 2008.

4.3. Travail faire : corriger le bug

  • Proposez finalement une correction du bug de Zune. Testez à nouveau votre programme pour vous assurer qu’il ne présente a priori plus d’erreur.


the_end.jpg