Paradigmes de programmation
I. Les différents paradigmes⚓︎
Il existe plusieurs façons de résoudre un problème à l'aide d'un langage de programmation. Une façon d'approcher un problème correspond à un style de programmation qu'on qualifie de paradigme. La plupart des langages de programmation généralistes modernes permettent d'utiliser plusieurs paradigmes et de les mélanger dans un même programme. On parle de langages multi-paradigmes.
On va présenter quelques paradigmes parmi les plus répandus.
🖽 Paradigme impératif⚓︎
Des notions familières
La programmation impérative repose sur des notions qui vous sont familières :
- la séquence d'instructions (les instructions d'un programme s'exécutent l'une après l'autre)
- l'affectation (on attribue une valeur Ă une variable, par exemple : a = 5)
- l'instruction conditionnelle (if / else)
- la boucle (while et for)
Le paradigme impératif
Dans le paradigme impératif, les données sont stockées dans des variables et le programme s'organise comme une séquence d'instructions qui vont modifier l'état du programme (données en mémoire et position dans le code source) depuis un état initial jusqu'à un état final correspondant à la solution du problème.
Quelques traits principaux du paradigme impératif
- La valeur d'une variable peut évoluer au cours de l'exécution : on parle de structure mutable
- Une instruction effectue une action pouvant modifier l'état du programme : ce peut être une affectation de variable (modification des données en mémoire), une structure de contrôle (test ou boucle qui modifie la position dans le code source)
- Un programme est une séquence d'instructions.
- Les unités de code réutilisables peuvent être stockées dans des fonctions ce qui facilite la lisibilité, la maintenance, la réutilisabilité. On parle alors de programmation structurée.
🖽 Paradigme objet⚓︎
En bref
- Le paradigme objet organise les données en une collection d'objets dont l'état interne (stocké dans des attributs) peut être modifié à l'aide de méthodes (des fonctions).
- Les objets sont instanciés à partir de classes qui étendent la notion de type du paradigme impératif.
- Un programme se présente comme une séquence d'interactions entre objets.
- Les objets sont souvent des structures mutables et le paradigme objet est une sorte de surcouche du paradigme impératif dont il reprend les concepts de variable, de séquence et de structure contrôle.
- La plupart des langages modernes comme Python, supportent ces deux paradigmes.
đź’ˇ A noter
Le paradigme objet permet de représenter des structures de données complexes en garantissant une propriété d'encapsulation
- l'utilisateur ne peut manipuler la structure qu'à travers une interface publique de façon indépendante de l'implémentation qui reste cachée et peut être modifiée sans impact sur le code client
- l'encapsulation facilite le travail en équipe sur de gros projets en permettant le découpage d'un programme en modules indépendants
🖽 Paradigme fonctionnel⚓︎
📓 Présentation
Le paradigme fonctionnel organise un programme comme un enchaînement d'évaluations de fonctions, chaque résultat produit en sortie d'une fonction étant pris en entrée de la fonction suivante.
Caractéristiques
Il en découle un certain nombre de traits spécifiques au paradigme fonctionnel :
- La valeur d'une variable ne change pas. Les structures de données sont immuables c'est-à -dire qu'elles ne peuvent être modifiées après leur création. Cela permet d'empêcher les effets de bord.
- Il n'existe donc pas d'instructions comme l'affectation qui peuvent modifier l'état du programme. Le calcul repose sur l'évaluation d'expressions, qui ont une valeur, et de fonctions, qui associent à une valeur, une autre valeur.
- Les fonctions sont des valeurs commes autres. Une fonction peut être argument d'une autre fonction, valeur de retour d'une autre fonction, stockée dans une structure de données.
- Une fonction peut donc s'appliquer à d'autres fonctions, on parle de fonction d'ordre supérieur : les analogies mathématiques sont la composition de fonction, la dérivation, l'intégration ...
- Les structures d'itération commes les boucles du paradigme impératif sont remplacées par la récursion.
- Les fonctions sont des fonctions pures c'est-à -dire qu'elles ne provoquent pas d'effets de bord lors de leur évaluation et que pour des entrées fixées, elles donnent toujours le même résultat en sortie. Cette propriété garantit la transparence référentielle c'est-à -dire que tout appel de fonction peut être remplacé par la valeur de son évaluation sans modifier le programme. Ceci ne serait pas garanti avec une fonction impure dont l'évaluation pourrait s'accompagner d'effets de bord en plus du calcul du résultat.
II. Le paradigme fonctionnel⚓︎
1. Exemple 1 : effet de bord⚓︎
Exemple 1
Exécuter le code ci-dessous. Que se passe-t-il ?
Solution
- La fonction
ajout_1
ne respecte pas le paradigme fonctionnel, car nous avons un effet de bord (la variableune_liste
est modifiée par la fonctionajout_1
). - La fonction
ajout_2
ne modifie aucune variable, elle crée un nouveau tableau. Elle ne produit pas d'effet de bord.
2. Exemple 2 : Transparence référentielle⚓︎
Exemple 2
Exécuter le code ci-dessous. Que se passe-t-il ?
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Solution
Les langages fonctionnels ont comme autre propriété la transparence référentielle. Ce terme recouvre le principe simple selon lequel le résultat du programme ne change pas si on remplace une expression par une expression de valeur égale. Ce principe est violé dans le cas de procédures à effets de bord puisqu'une telle procédure, ne dépendant pas uniquement de ses arguments d'entrée, ne se comporte pas forcément de façon identique à deux instants donnés du programme.
Ici, la fonction incremente_1
ne respecte donc pas cette proporiété de transparence référentielle. Elle ne respecte pas le
paradigme fonctionnel
Les fonctions pures⚓︎
Les fonctions pures
- Une fonction pure est une fonction qui ne modifie rien ; elle ne fait que renvoyer des valeurs en fonction de ses paramètres.
- Les modifications qu’une fonction peut effectuer sur l’état du système sont appelées effets de bord. Un affichage à l’écran est un exemple d’effet de bord.
Fonctions d'ordre supérieur⚓︎
Des fonctions passées en paramètres
Les fonctions sont des objets de première classe, ce qui signifie qu'elles sont manipulables aussi simplement que les types de base.
👉 Une fonction peut prendre des fonctions comme paramètres ou renvoyer une fonction comme résultat.
Exemple 3
Exécuter le code ci-dessous. Que se passe-t-il ?
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Solution
La fonction sorted
est une fonction d'ordre supérieur, qui prend en paramètre une fonction, comme ici clef_note
ou clef_nom
Fonctions anonymes et opérateur lambda
On peut écrire le même code de façon plus concise, en utilisant des fonctions anonymes, grâce à l'opérateur lambda
.
Par exemple la fonction :
def double(x):
return 2 * x
Peut être remplacée par
lambda x: 2 * x
On a "perdu" le nom de cette fonction, qui parfois n'est pas utile (d'oĂą le nom de fonction anonyme)
Si on le désire, on peut écrire :
double = lambda x: 2 * x
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Deux fonctions en paramètres
Exécuter le code ci-dessous, observer le résultat.
Vous pouvez expérimenter en mettant vos propres fonctions.
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Renvoyer une fonction⚓︎
Une fonction qui prend deux fonctions en pramètres et renvoie une fonction
Dans l'exemple précédant le résultat renvoyé était un réel, calculé à l'aide des paramètres (f, g, x)
.
Nous allons maintenant créer une fonction qui prend en paramètres seulement des fonctions, et renvoie une fonction.
Tester ci-dessous
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Solution
k
est une fonction, et on peut l'appeler avec n'importe quel nombre en paramètre.
les fonctions affines
On peut également définir une fonction qui renvoie une fonction. La fonction affine
prend en paramètres deux nombres
a
et b
et renvoie la fonction \(x \mapsto ax + b\).
Exécuter le code ci-dessous, puis recopier ligne par ligne dans la console (exécuter chaque ligne):
>>> f1 = affine(3, -2)
>>> f1(5)
>>> affine(-1, 4)(3)
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Qu'obtenez-vous ?
Solution
>>> f1 = affine(3, -2)
>>> f1(5)
13
>>> affine(-1, 4)(3)
1
f1
est la fonction affine définie par : pour tout \(x\) on a \(f1(x)=3x-2\)
\(f1(5)=15-2=13\)
On a ensuite créé la fonction affine définie par : pour tout \(x\) on a \(f(x)=-x+4\) .
On a ensuite déterminé l'image de 1 par cette fonction : \(-3+4=1\)
Exercice sur les fonctions du second degré
Écrire le code de la fonction trinome
qui prend en paramètre 3 nombres a
, b
et c
, avec a
non nul, et qui renvoie la fonction \(x \mapsto ax^2+ bx +c\).
>>> f = trinome(1, 1, 1) # x^2+x+1
>>> f(2) # 2^2+2+1 = 7
7
>>> f(0) # 0^2+0+1 = 1
1
>>> trinome(3, -1, 2)(6) # 3*6^2-6+2 = 104
104
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
III. Exemples de fonctions d'ordre supérieurs map
et filter
en python⚓︎
La fonction map
La fonction map est une fonction qui permet d’appliquer un traitement à tous les éléments d’un itérable. Cette fonction ne modifie pas l'objet de départ : elle renvoie un objet (itérable) encapsulant le résultat (le résultat n’est pas construit à l’appel) ; les valeurs sont calculées lorsqu’elles sont requises ; c’est une mise en œuvre du principe d’évaluation paresseuse. Le traitement est bien sûr spécifié via une fonction.
Appliquer une fonction à chaque élément d'un itérable - 1
Exécuter le code ci-dessous, observer le résultat.
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Appliquer une fonction à chaque élément d'un itérable - 2
On aurait pu utiliser une fonction anonyme. Exécuter le code ci-dessous, observer le résultat.
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
La fonction filter
La fonction filter
est un autre exemple de fonction d’ordre supĂ©rieur s’appliquant Ă
des objets itérables. Elle prend en premier paramètre une fonction à valeur booléenne appelée
filtre, et un objet itérable en deuxième paramètre. En résultat, elle renvoie un iterable ne
contenant que les valeurs de la liste pour lesquels le filtre renvoie la valeur True
.
Appliquer un filtre à chaque élément d'un itérable - 1
Exécuter le code ci-dessous, observer le résultat.
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Appliquer un filtre à chaque élément d'un itérable - 2
On aurait pu utiliser une fonction anonyme. Exécuter le code ci-dessous, observer le résultat.
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Crédits⚓︎
Frédéric Junier, Eduscol
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)