Programme comme donnée (Bac 🎯)⚓︎
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.
Sources et crédits pour ce cours
Pour préparer ce cours, j'ai utilisé :
- le manuel NSI et le manuel de MP2I/MPI chez Ellipses de Balabonski, Conchon, Filliâtre, Nguyen pour l'essentiel de la structure et du contenu.
- des articles du site Interstices à propos de la calculabilité :
- série d'articles de Jean-Louis Giavitto sur l'histoire de la définition du calcul dont cet article sur les calculateurs universels
- article de Jean-Gabriel Ganascia sur le lien entre calculabilité et décidabilité
🔖 Synthèse de ce qu'il faut retenir pour le bac
Programme en tant que donnée : détecteur d'erreur en Python⚓︎
Exercice 1 : détecteur d'erreur IndexError
Exercice inspiré du cours du manuel NSI chez Ellipses de Balabonski, Conchon, Filliâtre, Nguyen
Exception en Python et erreur IndexError
En Python une erreur qui se produit à l'exécution du programme s'appelle une exception. Les exceptions sont prédéfinies et sont organisées de façon hiérarchique en classes.
L'erreur IndexError
est levée en Python lorsque l'évaluation d'une expression nécessite l'accès à un indice dans une liste lis
Python qui se trouve en dehors de la plage licite des indices possibles [-len(lis), len(lis) - 1]
si on compte les index négatifs.
In [1]: lis = ["turing", "church", "kleene", "post"]
In [2]: lis[-1]
Out[2]: 'post'
In [3]: lis[-len(lis)]
Out[3]: 'turing'
In [4]: lis[len(lis) - 1]
Out[4]: 'post'
In [5]: lis[len(lis)]
Traceback (most recent call last):
File "/tmp/ipykernel_7767/3797187194.py", line 1, in <module>
lis[len(lis)]
IndexError: list index out of range
In [6]: lis[-len(lis) - 1]
Traceback (most recent call last):
File "/tmp/ipykernel_7767/3078907279.py", line 1, in <module>
lis[-len(lis) - 1]
IndexError: list index out of range
Question 1
- Exécuter le script ci-dessus. Expliquer l'erreur affichée.
- Qui était Edsger Dijsktra et à quelle occasion a-t-il prononcé son discours L'humble programmeur ?
L'exécution du script est interrompue par la levée de l'exception IndexError
car la liste lis
contient 6 éléments et lors de la dernière itération de boucle, on essaie d'accéder à l'index 6 qui est en dehors de la plage licite d'indices.
Edsger Dijsktra est un informaticien néerlandais dont les contributions en algorithmique sont très importantes. Il est célèbre pour son algorithme de recherche de plus court chemin depuis une origine unique dans un graphe pondéré dont les poids sont positifs. Il a reçu le prix Turing en 1972 et a prononcé à cette occasion un discours célèbre pour son humour et son autodérision, L'humble programmeur. Dans ce discours, Edsger Dijkstra aborde la complexité croissante des systèmes informatiques, soulignant que les programmeurs doivent reconnaître leurs limites pour mieux gérer cette complexité. Il prône une approche de la programmation qui favorise la simplicité et la structure, afin d'éviter les erreurs et de maintenir la compréhensibilité des programmes.
Question 2
prog1 = '''
humble_programmer = ['Program testing',
'can be a very effective way ',
'to show the presence of bugs,',
'but is hopelessly inadequate',
'for showing their absence.',
'Edsger Dijkstra']
for i in range(6):
print(humble_programmer[i])
'''
prog2 = '''
humble_programmer = ['Program testing',
'can be a very effective way ',
'to show the presence of bugs,',
'but is hopelessly inadequate',
'for showing their absence.',
'Edsger Dijkstra']
for i in range(7):
print(humble_programmer[i])
'''
prog3 = '''
humble_programmer = ['Program testing',
'can be a very effective way ',
'to show the presence of bugs,',
'but is hopelessly inadequate',
'for showing their absence.',
'Edsger Dijkstra']
i = 5
while True:
print(humble_programmer[i])
i = (i + 1) % 6
print(humble_programmer[6])
'''
def index_error_detecteur(donnée):
try:
exec(donnée) # exécution de donnée comme programme
return False
except IndexError:
return True
except:
return False
Dans cette question on va traiter deux programmes Python prog1
et prog2
comme des données passées en arguments sous forme de chaînes de caractères à un autre programme Python qui est la fonction index_error_detecteur
.
Programme comme donnée
Lorsqu'on exécute un programme Python, il est traité comme une donnée par un autre programme qui est l'interpréteur Python. Par exemple si un fichier hello.py
contient le code Python print('Hello World')
, on peut l'exécuter depuis la console de commande par défaut du système d'exploitation, en invoquant l'interpréteur Python avec la commande python
et en lui passant en argument le chemin vers le fichier contenant le programme.
Le programme est donc bien traité comme une donnée par l'interpréteur.
>>> python 'hello.py'
Hello World
Dans l'architecture de Von Neumann qui est la plus répandue parmi les architectures d'ordinateurs, les programmes et les données sont stockés dans la même mémoire centrale.
Le système d'exploitation (OS) est l'intermédiaire entre le matériel et les autres programmes. En particulier, pour démarrer l'exécution d'un programme, l'OS charge comme une donnée le code du programme depuis une mémoire externe dans la mémoire vive (RAM) puis initialise les registres du processeur. Le processus d'exécution du programme est alors créé et celui-ci peut à son tour manipuler des données.
Exécuter le script ci-dessus dans la console en ligne Basthon.
- Évaluer dans la console
index_error_detecteur(prog1)
. Expliquer le résultat affiché. - Évaluer dans la console
index_error_detecteur(prog2)
. Expliquer le résultat affiché. - Ajoutez une docstring à
index_error_detecteur
qui spécifie son comportement.
- L'évaluation de
index_error_detecteur(prog1)
affiche toutes les lignes contenues dans la listehumble_programmer
et renvoieFalse
car l'exécution du programme avecexec(prog1)
n'a levé aucune exception. - L'évaluation de
index_error_detecteur(prog2)
affiche toutes les lignes contenues dans la listehumble_programmer
et renvoieTrue
car l'exécution du programme avecexec(prog2)
a levé une exceptionIndexEror
avec un accès à l'indice 6 dans la listehumble_programmer
lors de la dernière itération de la boucle. index_error_detecteur
exécute le programme passé en argument (sous forme de chaîne de caractères) et renvoieTrue
si une exception/erreurIndexError
est levée etFalse
dans tous les autres cas : aucune exception n'est levée ou une exception différente deIndexError
est levée.
Python 3.11.2 (main, May 3 2023 04:00:05)
Type "help", "copyright", "credits" or "license" for mor
e information.
>>> # script executed
>>> index_error_detecteur(prog1)
Program testing
can be a very effective way
to show the presence of bugs,
but is hopelessly inadequate
for showing their absence.
Edsger Dijkstra
False
>>> index_error_detecteur(prog2)
Program testing
can be a very effective way
to show the presence of bugs,
but is hopelessly inadequate
for showing their absence.
Edsger Dijkstra
True
>>>
Question 3
Exécuter de nouveau le script de la question 2 dans la console en ligne Basthon.
- Évaluer dans la console
index_error_detecteur(prog3)
. Que se passe-t-il ? Expliquer. - La fonction
index_error_detecteur
permet-elle de détecter que n'importe quel programme Python passé en argument sous forme de chaîne de caractères, contient une instruction susceptible de provoquer uneIndexError
?
- L'évaluation de
index_error_detecteur(prog3)
ralentit d'abord l'affichage de la page dans le navigateur. Lorsqu'on arrête l'exécution du code qui bloque la page, on observe que l'exécution s'est traduite par un affichage en boucle infinie des lignes contenues dans la listehumble_programmer
. En effet, le corps de la bouclewhile True
est exécuté à l'infini et sans erreur puisque la variablei
croît de 0 à 5 puis revient à 0 avec l'opérateur modulo. Si l'instructionprint(humble_programmer[6])
après la boucle était exécutée, le programme serait interrompu par la levée d'une exceptionIndexError
puisque 6 est en dehors de la plage licite d'indices, mais cela ne se produira jamais à cause de la boucle infinie qui précède. - Dans le cas précédent une boucle infinie ne permet pas d'atteindre l'instruction qui lèverait une exception/erreur
IndexError
. Notre détecteur devrait donc renvoyerFalse
(pas d'erreur d'index à l'exécution) mais il boucle indéfiniment. Ce contre-exemple prouve que la fonctionindex_error_detecteur
ne permet pas de détecter une exception/erreurIndexError
daans l'exécution de n'importe quel programme Python.
Question 4
Supposons qu'il existe une fonction detecteur_parfait
qui pour n'importe quel programme Python passé en argument sous forme de chaîne de caractères, détermine si son exécution provoque une exception/erreur IndexError
.
Considérons le programme paradoxe_russell.py
ci-dessous :
from detecteur_parfait import detecteur_parfait
barbier = ['Un barbier rase tous les hommes',
'qui ne se rasent pas eux-mêmes',
'et seulement ceux-là.',
'Le barbier se rase-t-il lui-même ?']
f = open('paradoxe_russell.py')
prog = f.read()
f.close()
if detecteur_parfait(prog):
print("No IndexError detected")
else:
print("IndexError detected")
print(barbier[len(barbier)])
Si l'interpréteur Python exécute le programme paradoxe_russell.py
, une erreur IndexError
se produit-elle ?
On raisonne par disjonction des cas.
Premier cas :
Si l'erreur IndexError
se produit à l'exécution de paradoxe_russel.py
alors elle est détectée par detecteur_parfait
, la première branche du if
, qui ne contient pas barbier[len(barbier)]
, est exécutée et le programme se termine sans erreur IndexError
. On aboutit à une contradiction.
Second cas :
Si l'erreur IndexError
ne se produit pas à l'exécution de paradoxe_russell.py
alors elle n'est pas détectée par detecteur_parfait
, la seconde branche du if
, qui contient barbier[len(barbier)]
, est exécutée et le programme se termine avec une erreur IndexError
. On aboutit aussi à une contradiction.
Dans les deux cas on aboutit à une contradiction, on en déduit donc que la fonction detecteur_parfait
n'existe pas. C'est un exemple de raisonnement par l'absurde.
Le programme paradoxe_russell.py
est contradictoire cat il contient une autoréférence. En 1901, Bertrand Russell a mis en évidence un paradoxe autoréférent similaire dans la théorie des ensembles, considérée comme un fondement des mathématiques : "L'ensemble des ensembles n'appartenant pas à eux-mêmes appartient-il à lui-même ?".
Bertrand Russell, source : Wikipedia
# Tests
(insensible à la casse)(Ctrl+I)