7.8 KiB
Paradigmes de programmation
Un paradigme est à la programmation ce qu’un style culinaire est à la cuisine : il impose des règles, des outils, et une manière de faire.
Introduction
En programmation, il n’existe pas qu’une seule bonne façon de penser. Comme pour résoudre un problème de maths ou construire une maison, plusieurs approches sont possibles. Ces manières de faire s’appellent des paradigmes.
On appelle paradigme une méthode de programmation.
On peut regrouper les différents paradigmes en 2 catégories :
- la programmation impérative ;
- la programmation déclarative.
Lorsque l'on utilise la programmation impérative, on décrit le comment / la solution. On la retrouve dans plusieurs langage :
- Structurée (FORTRAN, C)
- Procédurale (FORTRAN, C)
- Objet (C++, Java, Python)
- Modulaire (C++, Java, Python)
Lorsque l'on utilise la programmation déclarative, on décrit le quoi / le problème. On la retrouve dans plusieurs langage :
- Descriptif (HTML, SQL)
- Fonctionnel (LISP, CAML)
- Logique (Prolog, SQL)
Programmation fonctionnelle
Les différences
La programmation impérative, bien que versatile, conduit rapidement à des programmes difficiles à comprendre et à maintenir, où la démonstration de la correction est impossible.
Ceci est du au fait qu’un traitement de données :
- dépend de paramètres extérieurs non maîtrisés : variables ;
- modifie les données, ou d’autres : affectation ;
- réalise une tâche complexe : une fonction complexe.
a = 3
répéter 5 fois :
a = a + 2
afficher a
La programmation fonctionnelle pure s’absout de ces problèmes :
- pas de variables
- pas d’affectation
- enchaînement de fonctions simples.
f : x -> x + 2
afficher f(f(f(f(f(3)))))
Le principe
Le but d'un traitement informatique est de produire des données à partir de données.
Le programme est une une fonction qui associe des données sortantes à des données entrantes.
f : données entrantes -> données sortantes
Pas d'état interne
Les données sont non mutables
Un traitement complexe s'obtiendra en composant plusieurs fonctions intermédiaires simples et une boucle s’obtiendra par une fonction qui s’appelle elle-même : récursivité.
En clair : La variable n'existe pas !.
Avantages et les inconvénients
Pour les avantages :
- Absence d’effets de bord.
- Toute fonction fournit un résultat prédictible qui est toujours le même.
- Clarifie l’interprétation du programme et facilite sa correction.
- Évite l’apparition de bugs non prédictibles.
En revanche, on retrouve des inconvénients :
- Un traitement classique nécessite un grand nombre de fonctions.
- Nuit au développement de grosses applications.
- Gourmand en ressources (stockage des résultats intermédiaires, pile des appels récursifs).
- Certaines fonctions doivent pouvoir modifier des données extérieures, ou en dépendre.
Effets de bord
Une fonction à effet de bord est une fonction qui modifie ou utilise une variable extérieure à son environnement local (variables globales, opérations d’entrées/sorties).
À n’utiliser que lorsque cela est nécessaire !
Les effets de bord peuvent être évité facilement grâce au principe de localité des variables et la programmation fonctionnelle.
# Fonction avec effets de bord :
def ajoute_k_a_n(k):
global n
n = n + k
return n
n = 2
print("Avec k=2 : ", ajoute_k_a_n(2))
print("Avec k=3 : ", ajoute_k_a_n(3))
# Fonction sans effets de bord :
def ajoute_k_a_n(n, k):
return n + k
print("Avec k=2 : ", ajoute_k_a_n(2))
print("Avec k=3 : ", ajoute_k_a_n(3))
Fonctions lambdas
λ-calcul (années 1930) : un système formel inventé par Alonzo Church qui fonde les concepts de fonction et d'application. Il s'agit du premier formalisme qui définit et caractérise les fonctions récursives.
lambda : x -> expression(x)
Tout comme la machine de Turing, le λ-calcul peut représenter n’importe quel calcul (hypothèse de Church-Turing, chaque approche peut être exprimée dans l’autre).
La programmation fonctionnelle puise son origine du λ-calcul alors que la programmation impérative puise son origine de la machine de Turing.
En python la lambda fonction se définie à l'aide du mot clé lambda :
add_a_b = lambda a,b: a + b
entre_10_et_20 = lambda x : True if (x > 10 and x < 20) else False
Appelée aussi fonction anonyme, il ne faut pas en abuser en Python, elle :
- est utile pour définir une fonction simple, proche d’une définition mathématique, en une ligne ;
- est utile pour créer la fonction de sortie d’une fonction d’ordre supérieur ;
- est utile pour composer les fonctions ;
- peut utiliser les mots clés if et else ;
- peut s’utiliser avec les fonctions d’ordres supérieur (exemple du tri de Python) ;
- peut s’utiliser conjointement à la compréhension de listes.
Fonctions et listes
Le mapping
Appliquer une fonction à chaque élément d'une liste.
f = lambda x: x*5
liste = [2, 7, -1]
resultat = map(f, liste)
La réduction
Combiner les éléments d’une liste en appliquant une fonction à l’aide d’un accumulateur (à l’état initial, acc prend la première valeur de la liste et x la deuxième, le résultat est stocké dans acc).
liste = [1, 3, 5, 6, 2]
f_somme = lambda acc, x: acc + x
somme = functools.reduce(f_somme, liste)
liste = [1, 3, 5, 6, 2]
f_maximum = lambda acc, x: acc if acc > x else x
maximum = functools.reduce(f_maximum, liste)
Le filtrage
Sélection les éléments d’une liste vérifiant un certain critère.
liste = [1, 3, 5, 6, 2]
f_filtre = lambda x: x >= 3
liste_filtree = filter(f_filtre, liste)
Boucler en programmation fonctionnelle
On utilise la récursivité, la fonction s’appelant elle-même un certain nombre de fois.
def ajoute_5(n, x):
if n == 0:
return x
else:
return ajoute_5(n - 1, x + 5)
resultat = ajoute_5(3, 2)
print(resultat)
On peut aussi plus simplement appliquer une réduction sur une liste de n éléments.
ajoute_5 = lambda acc, x: acc + 5
resultat = functools.reduce(ajoute_5, range(3), 2) # avec 2 pour valeur initiale de l'accumulateur
print(resultat)
Exemples de langages multi-paradigmes
- Python : impératif, objet, fonctionnel
- JavaScript : impératif, objet, fonctionnel
- Scala : objet + fonctionnel
- Rust : impératif + fonctionnel
Exercice
Voici une tâche simple : nous voulons faire cuire des pâtes.
Écrire la recette sous forme de code python :
- Version impérative : liste d’instructions étape par étape.
- Version fonctionnelle : une fonction pure qui prend un état (pâtes non cuites) et retourne un nouvel état (pâtes cuites).
- Version orientée objet : créer une classe Pates avec une méthode cuire().
Auteurs : Florian Mathieu, Timothée Decoster, Enzo Frémaux
Licence CC BY NC
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.
