Aller au contenu

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 ?

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

Solution
  • La fonction ajout_1 ne respecte pas le paradigme fonctionnel, car nous avons un effet de bord (la variable une_liste est modifiĂ©e par la fonction ajout_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 ?

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

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 ?

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

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
tester ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

Deux fonctions en paramètres

Exécuter le code ci-dessous, observer le résultat.

Vous pouvez expérimenter en mettant vos propres fonctions.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

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

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

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):

Recopier
>>> f1 = affine(3, -2)
>>> f1(5)
>>> affine(-1, 4)(3)
###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

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\).

Exemples d'utilisation
>>> 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

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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 : 5/5

.128013f:S=dyr/oxpg2mcb1wve l,*+P)tiknua(sh050f0u0C0H0D0w0J0v0p0w0H0J0J0e010C0D0l010406050J0G0o0o0H0h0g040d0j0w0G0#0j0F050i0,0.0:0=0*0l0405150~180i150*0f0D0t0T0V0X0Z0K0D0m0K0w1m0K0C0(050O0q0w0u1h0W0Y011l1n1p1n0C1v1x1t0C0h160C0K0T0^0J0l0H0F0Z0n011z1j010b0Q0u0F0H0o0u1t1S1U1Z1B1$1x1)1+0(0a0v0A0h0j0l0j0J0D0{0F0v0M1Q0h0h0u0p230~1.0F160i1O2g1L1N1M1u0f1:0Z1p0F1(201t1e1g0U1A2q0D2s0F0j2w1t0l29162e2g2K0+1T242y1!2D0h0/0w0(0r2d2O0)2N1/2Q1B2S2U0(0n2Y1U2g2H0u2g2w2j0f1N2o2%0Z0p2E1,162?172I2#2f2-352}0M2J2O2p010E0(0M0b363a2$1i1B0s0(0v3i343c0F0b0(1L2B0j1+3q2e3c0%040I3A3b2{010F0(0H3G3k2z013D0x3i3p3B3I3K040q3N2P3l0Z3R3T3r3W0(0p3!3C0(0B0c3i060v3^3U3H3$3d0(290C0G0h0}0 2Z3`3O1!3e040V0o0q0f3M442.463#3P3X0k3.3I3D3=4g2f4i3s3L4n3|0j0(0y4w4k0(4m4r3j4j1!4y044A4F3*3|4l3)3V4x0(0z4Q3{4C3Y4B4I4z4Z2(4D4V471B4J4U4F4t3+043-4F0*0i382;19330i312h2^0~2k510H1w4`4}1f2!4}0N0P0R04.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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 : 5/5

.128013f:S=dyr/oxpg2mcb1ve l,*+Ptiknuash050f0t0A0F0B0v0G0u0p0v0F0G0G0e010A0B0l010406050G0E0o0o0F0h0g040d0j0v0E0Y0j0D050i0)0+0-0/0%0l0405120{150i120%0f0B0s0Q0S0U0W0H0B0m0H0v1j0H0A0#050L0q0v0t1e0T0V011i1k1m1k0A1s1u1q0A0h130A0H0Q0=0G0l0F0D0W0n011w1g010b0N0t0D0F0o0t1q1P1R1W1y1Z1u1$1(0#0a0u0z0h0j0l0j0G0B0^0D0u0J1N0h0h0t0p200{1+0D130i1L2d1I1K1J1r0f1-0W1m0D1#1}1q1b1d0R1x2n0B2p0D0j2t1q0l26132b2d2H0(1Q212v1X2A0h0,0v0#0r2a2L2d2E0t2d2t2g0f1K2l2N1y0p2B1)132#142F2K1R2I2W052,0J2G2L2m010D0#1I2y0j1(2V2@0u2?2M1f1y0j0#0e372c392b2 0C0#0S0o0q0f0F3h043j2~2*0W31043s0|2_3k3x010!040w3t3v1,3F3z0q3t3a2 3H3J3C383R3N0#0p3Q3E3c0W3H0c3K3X3%013m043o3q3B2H3L3b2w300#0k3#3w3-3)3+3$3`3z3?3D3 3`3e040x3~3M3-3z3}3V2c3,490#4c4i2}4e443|42481X4a0y4t4q2O0#3P4o4k4v4m4d3_4A044h3@4E3d0#4x4o3^2 3z3!4o0%0i2{2Z162=0i2:2e2%0{2h4+0F1t4!4%1c0$0{0J0L0N0G04.

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.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

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.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

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.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

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.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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

Crédits⚓︎

Frédéric Junier, Eduscol