Programming errors
Reading time10 minEn bref
Résumé de l’article
Dans cet article, nous analysons certaines erreurs qui peuvent survenir lors de l’écriture d’un programme, et discutons du cas particulier des programmes avec des éléments aléatoires.
Points clés
-
Il existe deux types d’erreurs : les erreurs syntaxiques et les erreurs d’exécution.
-
Les nombres aléatoires peuvent rendre le débogage difficile, il est donc recommandé de fixer une graine (seed).
Contenu de l’article
La programmation est un processus incrémental, ce qui signifie que le code doit être construit étape par étape, chaque étape devant être testée avant de passer à la suivante. Le temps de développement peut être grossièrement divisé en trois phases itératives :
- Codage.
- Débogage.
- Documentation.
Et le débogage prend définitivement beaucoup de temps de développement. Une compétence importante à acquérir est d’identifier efficacement la source des bugs et de les corriger. De nombreux outils ont été conçus pour vous aider à déterminer rapidement où se trouvent les erreurs de programmation et les bugs. Mais avant de découvrir ces outils spécifiques, il est obligatoire d’apprendre à interpréter les messages d’erreur rendus par l’interpréteur (resp. compilateur).
L’interpréteur Python peut détecter des erreurs lors de l’interprétation de votre code qui sont principalement de deux types.
1 — Erreurs syntaxiques
Les erreurs syntaxiques sont situées dans le texte de votre code, et peuvent être détectées sans exécuter le code (pendant les phases d’analyse/compilation). Elles sont assez explicitement expliquées par l’interpréteur qui vous fournit une description de l’erreur et la ligne où elle se produit.
Considérez le programme suivant :
# The string to manipulate
s = "IMT Atlantique"
print(s)
# Loop to append to a new string
result = ""
for i j in range(len(s)):
result = s[i] + result
# Print the result
print("Reversed string:", result)
/**
* To run this code, you need to have Java installed on your computer, then:
* - Create a file named `Main.java` in a directory of your choice.
* - Copy this code in the file.
* - Open a terminal in the directory where the file is located.
* - Run the command `javac Main.java` to compile the code.
* - Run the command `java -ea Main` to execute the compiled code.
* Note: '-ea' is an option to enable assertions in Java.
*/
public class Main {
/**
* This is the entry point of your program.
* It contains the first codes that are going to be executed.
*
* @param args Command line arguments received.
*/
public static void main(String[] args) {
// The string to manipulate
String s = "IMT Atlantique";
System.out.println(s);
// Loop to append to a new string
String result = "";
for (int i j = 0; i < s.length(); i++) {
result = s.charAt(i) + result;
}
// Print the result
System.out.println("Reversed string: " + result);
}
}
Ici, l’interpréteur nous indique qu’à la ligne 7 de votre code, le token j
est inattendu à cette position, car il ne peut pas suivre un autre token correspondant à un nom de variable (i.e., i
) :
File "session_1.py", line 7
for i j in range(len(s)):
^
SyntaxError: invalid syntax
Main.java:25: error: ';' expected
for (int i j = 0; i < s.length(); i++)
^
Main.java:25: error: not a statement
for (int i j = 0; i < s.length(); i())
^
Main.java:25: error: ')' expected
for (int i j = 0; i < s.length(); i())
^
Main.java:25: error: ';' expected
for (int i j = 0; i < s.length(); i())
^
4 errors
2 — Erreurs d’exécution
Les erreurs d’exécution apparaissent uniquement lorsque le programme est en cours d’exécution (pendant la phase d’exécution). Elles sont plus difficiles à résoudre car elles dépendent souvent du contexte d’exécution. Voici quelques exemples pouvant causer ce type d’erreur :
- Une opération non autorisée compte tenu des types actuels des valeurs concernées.
- Une opération d’entrée/sortie sur une ressource indisponible (ex., un fichier).
- Un accès à une zone mémoire indisponible.
Considérez le programme suivant :
# Ask user at runtime for a value
den = input("Please enter the value of the denominator")
# Perform a computation
result = 42 / den
print(result)
// Needed imports
import java.util.Scanner;
/**
* To run this code, you need to have Java installed on your computer, then:
* - Create a file named `Main.java` in a directory of your choice.
* - Copy this code in the file.
* - Open a terminal in the directory where the file is located.
* - Run the command `javac Main.java` to compile the code.
* - Run the command `java -ea Main` to execute the compiled code.
* Note: '-ea' is an option to enable assertions in Java.
*/
public class Main {
/**
* This is the entry point of your program.
* It contains the first codes that are going to be executed.
*
* @param args Command line arguments received.
*/
public static void main (String[] args) {
// Ask user at runtime for a value
Scanner scanner = new Scanner(System.in);
System.out.print("Please enter the value of the denominator: ");
String denStr = scanner.nextLine();
double den = Double.parseDouble(denStr);
// Perform a computation
double result = 42 / den;
System.out.println(result);
}
}
Bien que syntaxiquement correct, la troisième ligne du code suivant générera une erreur d’exécution de type division par zéro si 0
est assigné à den
.
3 — Le cas des nombres aléatoires
Parfois, les programmes utilisent des nombres aléatoires. Considérez par exemple le programme suivant :
# Needed imports
import random
# Fill a list with 10 random numbers
numbers = [random.randint(-10, 10) for i in range(10)]
# Generate an error if 0 is in the list
if 0 in numbers:
raise ValueError("Zero is in the list")
# Otherwise, print the list
print(numbers)
// Needed imports
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* To run this code, you need to have Java installed on your computer, then:
* - Create a file named `Main.java` in a directory of your choice.
* - Copy this code in the file.
* - Open a terminal in the directory where the file is located.
* - Run the command `javac Main.java` to compile the code.
* - Run the command `java -ea Main` to execute the compiled code.
* Note: '-ea' is an option to enable assertions in Java.
*/
public class Main {
/**
* This is the entry point of your program.
* It contains the first codes that are going to be executed.
*
* @param args Command line arguments received.
*/
public static void main (String[] args) {
// Create a random number generator
Random random = new Random();
// Fill a list with 10 random numbers
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
numbers.add(random.nextInt(11) - 10);
}
// Generate an error if 0 is in the list
if (numbers.contains(0)) {
throw new IllegalArgumentException("Zero is in the list");
}
// Otherwise, print the list
System.out.println(numbers);
}
}
Parfois, lors de l’exécution de votre programme, vous obtiendrez la sortie suivante :
[3, 1, -2, -4, 8, 4, -9, -2, 3, 3]
[-9, -1, -6, -7, -4, -7, -10, -8, -9, -10]
Ou parfois :
[-2, -7, -9, -1, -6, -7, -7, -6, -2, -7]
[-5, -4, -3, -9, -10, -5, -1, -1, -7, -5]
Ou :
Traceback (most recent call last):
File "session_1.py", line 9, in <module>
raise ValueError("Zero is in the list")
ValueError: Zero is in the list
Exception in thread "main" java.lang.IllegalArgumentException: Zero is in the list
at Main.main(Main.java:37)
Eh bien, c’est attendu. Cependant, imaginez un scénario plus complexe où l’erreur factice ci-dessus est une erreur vraiment importante que vous devez éviter. Il sera assez difficile à déboguer, car parfois votre programme fonctionnera comme prévu, et parfois il plantera à cause d’une erreur sémantique.
En pratique, il est possible de forcer une série de nombres aléatoires à toujours être la même. En effet, les nombres aléatoires sont en fait pseudo-aléatoires. Ils sont générés en utilisant une série déterministe qui possède de bonnes propriétés d’aléa, initialisée à l’aide d’une “graine” (seed), i.e., une première valeur initiale. Si la graine est connue, alors toute la série peut être prédite. Mieux encore, si la graine peut être fixée à une valeur arbitraire, alors on peut garantir que toutes les exécutions du même programme conduiront à la même génération de nombres aléatoires.
Chaque générateur de nombres aléatoires fournit un moyen de fixer la graine à une valeur donnée (ici, 42
).
Cela peut être fait comme suit :
# Needed imports
import random
# Set the seed
random.seed(42)
# Fill a list with 10 random numbers
numbers = [random.randint(-10, 10) for i in range(10)]
# Generate an error if 0 is in the list
if 0 in numbers:
raise ValueError("Zero is in the list")
# Otherwise, print the list
print(numbers)
// Needed imports
import java.util.Random;
/**
* To run this code, you need to have Java installed on your computer, then:
* - Create a file named `Main.java` in a directory of your choice.
* - Copy this code in the file.
* - Open a terminal in the directory where the file is located.
* - Run the command `javac Main.java` to compile the code.
* - Run the command `java -ea Main` to execute the compiled code.
* Note: '-ea' is an option to enable assertions in Java.
*/
public class Main {
/**
* This is the entry point of your program.
* It contains the first codes that are going to be executed.
*
* @param args Command line arguments received.
*/
public static void main (String[] args) {
// Create a random number generator
Random random = new Random();
// Set the seed
random.setSeed(42);
// Fill a list with 10 random numbers
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
numbers.add(random.nextInt(11) - 10);
}
// Generate an error if 0 is in the list
if (numbers.contains(0)) {
throw new IllegalArgumentException("Zero is in the list");
}
// Otherwise, print the list
System.out.println(numbers);
}
}
Relançons notre programme trois fois encore
[10, -7, -10, -2, -3, -3, -6, -7, 7, -8]
Exception in thread "main" java.lang.IllegalArgumentException: Zero is in the list
at Main.main(Main.java:36)
[10, -7, -10, -2, -3, -3, -6, -7, 7, -8]
Exception in thread "main" java.lang.IllegalArgumentException: Zero is in the list
at Main.main(Main.java:36)
[10, -7, -10, -2, -3, -3, -6, -7, 7, -8]
Exception in thread "main" java.lang.IllegalArgumentException: Zero is in the list
at Main.main(Main.java:36)
Comme vous le voyez, le résultat est toujours le même. Maintenant, ce que vous pouvez faire pour déboguer votre programme est de trouver une valeur de la graine qui conduit à une erreur. Ensuite, gardez cette graine fixée et essayez de voir ce qui se passe avec cette configuration aléatoire problématique particulière !
Quelques remarques importantes :
-
La liste que vous obtenez peut être différente d’un ordinateur à un autre, car le générateur de nombres aléatoires utilisé peut être différent. Fixer la graine ne garantit la reproductibilité que sur une seule machine.
-
Chaque générateur de nombres aléatoires possède sa propre graine. Si vous utilisez des fonctions des bibliothèques
random
etnumpy.random
par exemple, vous devrez fixer les graines des deux générateurs pour garantir la reproductibilité. -
Vous ne pouvez pas utiliser la graine aléatoire pour modifier les performances de vos programmes. Par exemple, si votre algorithme qui incorpore des éléments aléatoires résout un problème mieux avec
seed = 42
qu’avecseed = 0
, il est injuste de rapporter les performances obtenues dans le premier cas. En effet, cela signifie simplement que42
conduit à une initialisation chanceuse de votre algorithme pour cette instance particulière du problème, mais ce ne sera pas le cas en général. Le rapport des performances pour les algorithmes aléatoires doit toujours être fait en exécutant le programme de nombreuses fois et en fournissant des moyennes et des intervalles de confiance !
Pour aller plus loin
On dirait que cette section est vide !
Y a-t-il quelque chose que vous auriez aimé voir ici ? Faites-le nous savoir sur le serveur Discord ! Peut-être que nous pourrons l’ajouter rapidement. Sinon, cela nous aidera à améliorer le cours pour l’année prochaine !
Pour aller au-delà
-
C’est une manière formelle de décrire le comportement d’un programme pour montrer sa correction.
-
Un paradigme de programmation reposant sur les notions ci-dessus pour concevoir des logiciels.