Aller au contenu

Programme comme donnée (Bac 🎯)⚓︎

Licence Creative Commons
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.

programme

Sources et crédits pour ce cours

Pour préparer ce cours, j'ai utilisé :

🔖 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.

🐍 Console Python
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

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : /∞

  1. Exécuter le script ci-dessus. Expliquer l'erreur affichée.
  2. 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

🐍 Script Python
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.

🐍 Console Python
>>> 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.

alt

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.

  1. Évaluer dans la console index_error_detecteur(prog1). Expliquer le résultat affiché.
  2. Évaluer dans la console index_error_detecteur(prog2). Expliquer le résultat affiché.
  3. Ajoutez une docstring à index_error_detecteur qui spécifie son comportement.
  1. L'évaluation de index_error_detecteur(prog1) affiche toutes les lignes contenues dans la liste humble_programmer et renvoie False car l'exécution du programme avec exec(prog1) n'a levé aucune exception.
  2. L'évaluation de index_error_detecteur(prog2) affiche toutes les lignes contenues dans la liste humble_programmer et renvoie True car l'exécution du programme avec exec(prog2) a levé une exception IndexEror avec un accès à l'indice 6 dans la liste humble_programmer lors de la dernière itération de la boucle.
  3. index_error_detecteur exécute le programme passé en argument (sous forme de chaîne de caractères) et renvoie True si une exception/erreur IndexError est levée et False dans tous les autres cas : aucune exception n'est levée ou une exception différente de IndexError est levée.
🐍 Console Python
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.

  1. Évaluer dans la console index_error_detecteur(prog3). Que se passe-t-il ? Expliquer.
  2. 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 une IndexError ?
  1. 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 liste humble_programmer. En effet, le corps de la boucle while True est exécuté à l'infini et sans erreur puisque la variable i croît de 0 à 5 puis revient à 0 avec l'opérateur modulo. Si l'instruction print(humble_programmer[6]) après la boucle était exécutée, le programme serait interrompu par la levée d'une exception IndexError 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.
  2. 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 renvoyer False (pas d'erreur d'index à l'exécution) mais il boucle indéfiniment. Ce contre-exemple prouve que la fonction index_error_detecteur ne permet pas de détecter une exception/erreur IndexError 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 :

🐍 Script Python
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 ?".

alt

Bertrand Russell, source : Wikipedia