Principes des Langages de Programmation
Programmation fonctionnelle

John Samuel
CPE Lyon

Année: 2026-2027
Courriel: john.samuel@cpe.fr
Creative Commons License

4. Principes des Langages de Programmation : Objectifs

Objectifs

4.1. Fonctions pures et effets de bord

Fonctions pures, effets de bord, immuabilité

/* impur : modifie le tableau de l'appelant */
void doubler(int t[], size_t n) {
  for (size_t i = 0; i < n; i++)
    t[i] *= 2;
}
/* pur : produit une nouvelle valeur */
int double_de(int x) {
  return 2 * x;
}

La seconde se teste sans contexte, se met en cache, et s'exécute en parallèle sans risque. La première non : c'est tout l'intérêt du paradigme.

4.2. Les expressions lambda

Les expressions lambda

Caractéristiques clés

  1. Anonyme : Les lambdas n’ont pas de noms explicites.
  2. En ligne : Elles sont définies directement dans le code environnant.
  3. Usage unique : Elles réalisent généralement une opération spécifique et courte.
  4. Fermeture : Elles peuvent capturer les variables du contexte dans lequel elles ont été définies.

4.2. Les expressions lambda

Syntaxe

La notation d'origine vient du lambda-calcul (Church, 1936) :

λx. x * 2

Chaque langage la transcrit à sa façon — il n'y a pas de syntaxe « générale » :

lambda x: x * 2        # Python : mot-clé lambda, ':' avant le corps
x => x * 2             // JavaScript, C# : flèche
x -> x * 2             // Java : flèche fine
[](int x) { ... }      // C++ : liste de capture
\x -> x * 2            -- Haskell : le '\' rappelle le λ

Dans tous les cas, les paramètres sont les entrées et l’expression est l’opération effectuée lorsque la lambda est invoquée.

4.2. Les expressions lambda

Exemples dans différents langages de programmation

Voici quelques exemples d’expressions lambda dans différents langages de programmation :

4.2. Les expressions lambda

Python

# Exemple : additionner deux nombres
addition = lambda x, y: x + y
résultat = addition(3, 4)  # Résultat : 7

Voici une décomposition de l’expression lambda :

addition = lambda x, y: x + y

4.2. Les expressions lambda

Python

# Exemple : additionner deux nombres
addition = lambda x, y: x + y
résultat = addition(3, 4)  # Résultat : 7

Lorsque vous assignez cette expression lambda à une variable (addition), vous pouvez l’appeler comme une fonction régulière en passant les arguments requis (3 et 4 dans votre exemple).

résultat = addition(3, 4)

4.2. Les expressions lambda

Python

# Lambda à l'intérieur d'une fonction map
nombres = [1, 2, 3, 4]
carrés = list(map(lambda x: x ** 2, nombres))  # Résultat : [1, 4, 9, 16]

La fonction map est une fonction intégrée en Python qui applique une fonction à chaque élément d’une liste (ou d’un autre itérable) et renvoie une nouvelle liste avec les résultats. Dans cet exemple, une expression lambda est utilisée pour définir une fonction anonyme qui prend un élément x en entrée et renvoie son carré (x ** 2).

4.2. Les expressions lambda

Python

# Lambda à l'intérieur d'une fonction map
nombres = [1, 2, 3, 4]
carrés = list(map(lambda x: x ** 2, nombres))  # Résultat : [1, 4, 9, 16]

L’exécution de cette ligne de code produit les résultats suivants :

  1. La fonction map itère sur chaque élément de la liste nombres.
  2. Pour chaque élément x, la fonction lambda est appelée avec x comme argument.
  3. La fonction lambda renvoie le carré de x.
  4. Le résultat est ajouté à la liste carrés.

Le résultat final est une liste contenant les carrés de chaque élément de la liste nombres.

4.2. Les expressions lambda

JavaScript

// Exemple : additionner deux nombres
const addition = (x, y) => x + y;
const résultat = addition(3, 4);  // Résultat : 7

// Lambda passée comme argument à la fonction map
const nombres = [1, 2, 3, 4];
const carrés = nombres.map(x => x ** 2);  // Résultat : [1, 4, 9, 16]

4.2. Les expressions lambda

C++

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    // Exemple : additionner deux nombres
    auto addition = [](int x, int y) { return x + y; };
    int résultat = addition(3, 4);  // Résultat : 7

}

4.2. Les expressions lambda

C++

#include <iostream>
#include <vector>
#include <algorithm>

int main() {

    // Lambda dans une fonction for_each
    std::vector<int> nombres = {1, 2, 3, 4};
    std::for_each(nombres.begin(), nombres.end(),
                  [](int &n) { n *= n; });

    for (int n : nombres) {
        std::cout << n << " ";  // Résultat : 1 4 9 16
    }
}

Remarque : for_each avec int &n modifie le vecteur en place — un effet de bord, l'inverse de la fonction pure vue plus haut. Sans effet de bord, on construit une nouvelle valeur avec std::transform.

4.3. Les expressions lambda en Python

1. Affectation à une variable

Une expression lambda peut être assignée à une variable, ce qui permet de la réutiliser comme une fonction ordinaire.

Exemple : Additionner deux nombres

# Définition de la lambda
addition = lambda x, y: x + y

# Utilisation de la lambda
résultat = addition(3, 4)
print(résultat)  # Output : 7

4.3. Les expressions lambda en Python

2. Passage en Argument à une Fonction

Les lambdas sont souvent utilisées comme arguments pour d’autres fonctions, notamment dans des fonctions de rappel (callbacks) ou des méthodes qui prennent une fonction en paramètre.

Un callback est une fonction qui est passée en argument à une autre fonction, afin d’être appelée plus tard par cette dernière. En d’autres termes, un callback est une fonction qui est “appelée en retour” par une autre fonction.

4.3. Les expressions lambda en Python

2. Passage en Argument à une Fonction

Exemple : callback

def appel_fonction(callback, param):
    result = callback(param)
    print(f"Le résultat de la fonction est : {result}")

# Définition d'une fonction lambda comme callback
callback = lambda x: x ** 2

# Appel de la fonction avec le callback
appel_fonction(callback, 5)

4.3. Les expressions lambda en Python

2. Passage en Argument à une Fonction

Exemple : callback

appel_fonction prend deux paramètres : callback, une fonction, et param, la valeur à lui passer. La lambda joue ici le rôle de callback : elle reçoit x et renvoie son carré.

Appelée avec 5, elle produit :

Le résultat de la fonction est : 25

La lambda est définie à la volée et utilisée comme une fonction ordinaire, mais elle reste anonyme. C'est l'idiome des callbacks : gestion d'événements, tris, traitements paramétrés.

4.3. Les expressions lambda en Python

2. Passage en Argument à une Fonction

Les lambdas sont souvent utilisées comme arguments pour d’autres fonctions, notamment dans des fonctions de rappel (callbacks) ou des méthodes qui prennent une fonction en paramètre.

Exemple : Utilisation avec map

# Liste de nombres
nombres = [1, 2, 3, 4]

# Utilisation de lambda avec map pour calculer les carrés
carrés = list(map(lambda x: x ** 2, nombres))
print(carrés)  # Output : [1, 4, 9, 16]

4.3. Les expressions lambda en Python

2. Passage en Argument à une Fonction

Exemple : Utilisation avec filter

# Liste de nombres
nombres = [1, 2, 3, 4, 5, 6]

# Utilisation de lambda avec filter pour sélectionner les nombres pairs
pairs = list(filter(lambda x: x % 2 == 0, nombres))
print(pairs)  # Output : [2, 4, 6]

4.3. Les expressions lambda en Python

2. Passage en Argument à une Fonction

Exemple : Utilisation avec filter

Dans ce cas, la fonction utilisée est une expression lambda :

lambda x: x % 2 == 0

Cette lambda prend un seul paramètre x (qui représente un élément de la liste) et renvoie True si x est pair, c’est-à-dire lorsque x % 2 == 0.

4.3. Les expressions lambda en Python

2. Passage en Argument à une Fonction

Exemple : Utilisation avec filter

4.3. Les expressions lambda en Python

3. Retour d’une Fonction

Une fonction peut retourner une expression lambda, ce qui permet de créer des fonctions dynamiques. Cela est particulièrement utile pour créer des fonctions personnalisées à la volée.

Exemple : Création d’un multiplicateur

def créer_multiplicateur(n):
    return lambda x: x * n

4.3. Les expressions lambda en Python

3. Retour d’une Fonction

Exemple : Création d’un multiplicateur

# Création d'une lambda qui multiplie par 2
doubler = créer_multiplicateur(2)
print(doubler(5))  # Output : 10

# Création d'une lambda qui multiplie par 3
tripler = créer_multiplicateur(3)
print(tripler(5))  # Output : 15

4.3. Les expressions lambda en Python

4. Utilisation dans des Collections ou des Itérations

Les expressions lambda sont souvent utilisées pour effectuer des opérations sur des collections, telles que le tri, la transformation ou le filtrage des éléments.

Exemple : Tri d’une liste de tuples par le deuxième élément

# Liste de tuples
éléments = [(1, 'b'), (2, 'a'), (3, 'c')]

# Tri de la liste en utilisant lambda comme clé
éléments_triés = sorted(éléments, key=lambda x: x[1])
print(éléments_triés)  # Output : [(2, 'a'), (1, 'b'), (3, 'c')]

4.3. Les expressions lambda en Python

5. Fonctions Anonymes Temporaires

Parfois, il est nécessaire d’utiliser une fonction une seule fois sans la définir explicitement. Les lambdas sont idéales pour ces cas temporaires.

Exemple : Calcul rapide sans assignation

# Utilisation immédiate de la lambda
résultat = (lambda x, y: x * y)(4, 5)
print(résultat)  # Output : 20

4.3. Les expressions lambda en Python

6. Fermetures (Closures)

Les expressions lambda peuvent capturer des variables de leur environnement, créant ainsi des fermetures. Cela permet à la lambda de se souvenir des valeurs des variables au moment de sa création.

Exemple

def externe(x):
    def interne(y):
        return x + y
    return interne

4.3. Les expressions lambda en Python

6. Fermetures (Closures)

Exemple

# Création d'une closure
closure1 = externe(5)
closure2 = externe(10)

# Appel des closures
print(closure1(3))  # Affiche 8
print(closure2(3))  # Affiche 13

4.3. Les expressions lambda en Python

6. Fermetures (Closures)

Les expressions lambda peuvent capturer des variables de leur environnement, créant ainsi des fermetures. Cela permet à la lambda de se souvenir des valeurs des variables au moment de sa création.

Exemple

def externe(x):
    return lambda y: x + y

Dans cet exemple, la fonction externe prend un paramètre x et retourne une expression lambda qui prend un paramètre y. L’expression lambda retourne la somme de x et y.

4.3. Les expressions lambda en Python

6. Fermetures : le piège de la capture tardive

Une fermeture capture la variable, pas sa valeur au moment de la création :

fs = [lambda: i for i in range(3)]
print([f() for f in fs])   # [2, 2, 2] — et non [0, 1, 2] !

# Correction : forcer la capture par valeur via un paramètre par défaut
fs = [lambda i=i: i for i in range(3)]
print([f() for f in fs])   # [0, 1, 2]

Les trois lambdas partagent la même variable i, lue seulement à l'appel — après la fin de la boucle. C'est l'erreur de fermeture la plus fréquente, en Python comme en JavaScript (var) : les valeurs par défaut, elles, sont évaluées à la définition.

4.3. Les expressions lambda en Python

6. Fermetures (Closures)

Exemple

La fonction externe retourne la fonction lambda, qui est une closure. La closure a accès à la variable x qui a été définie dans la fonction externe, même après que la fonction externe ait terminé son exécution.

Lorsque nous créons des instances de la closure en appelant la fonction externe avec des valeurs différentes pour x, nous obtenons des closures qui ont des valeurs différentes pour x. Cependant, chaque closure a toujours accès à la valeur de x qui a été définie lors de sa création.

4.3. Les expressions lambda en Python

7. Utilisation dans des fonctions d’ordre supérieur

Les lambdas sont très utiles dans les fonctions d’ordre supérieur, c’est-à-dire des fonctions qui prennent d’autres fonctions en paramètre ou qui en retournent.

Une fonction d'ordre supérieur (ou higher-order function en anglais) est une fonction qui prend une ou plusieurs fonctions en paramètre, ou qui retourne une fonction en tant que résultat. Ces fonctions sont un concept clé en programmation fonctionnelle, permettant de créer du code plus flexible et réutilisable.

Caractéristiques des fonctions d'ordre supérieur

4.3. Les expressions lambda en Python

7. Utilisation dans des fonctions d’ordre supérieur

Exemple : Utilisation avec reduce

from functools import reduce

# Liste de nombres
nombres = [1, 2, 3, 4]

# Utilisation de lambda avec reduce pour calculer la somme
somme = reduce(lambda acc, x: acc + x, nombres)
print(somme)  # Output : 10

4.3. Les expressions lambda en Python

7. Utilisation dans des fonctions d’ordre supérieur

Exemple : Utilisation avec reduce

somme = reduce(lambda acc, x: acc + x, nombres)

Fonctionnement de reduce :

4.3. Les expressions lambda en Python

7. Utilisation dans des fonctions d’ordre supérieur

Exemple : Utilisation avec reduce

somme = reduce(lambda acc, x: acc + x, nombres)

Dans ce cas, la fonction passée est une expression lambda :

lambda acc, x: acc + x

Cette lambda prend deux paramètres : - acc (l’accumulateur) : C’est le total accumulé à chaque étape de l’itération. - x : C’est l’élément courant de la liste.

Ce que fait la lambda : Elle additionne l’accumulateur acc et l’élément courant x à chaque étape.

4.3. Les expressions lambda en Python

7. Utilisation dans des fonctions d’ordre supérieur

Exemple : Utilisation avec reduce

Comment reduce fonctionne étape par étape :

Le résultat final est donc la somme de tous les éléments de la liste : 10.

4.3. Les expressions lambda en Python

Résumé des avantages des expressions lambda en Python

  1. Concision : Permettent d’écrire des fonctions simples en une seule ligne.
  2. Flexibilité : Peuvent être utilisées à divers endroits où une fonction est requise.
  3. Lisibilité : Facilitent la compréhension du code en évitant des définitions de fonctions multiples pour des opérations simples.

Les expressions lambda facilitent l’écriture de code concis, surtout combinées avec des fonctions d’ordre supérieur.

Trois limites à connaître :


4.4. Fonctions d'ordre supérieur

map, filter, reduce

Une fonction d'ordre supérieur prend une fonction en paramètre, ou en renvoie une.

from functools import reduce
nombres = [1, 2, 3, 4, 5, 6]

carres = list(map(lambda x: x ** 2, nombres))   # [1, 4, 9, 16, 25, 36]
pairs  = list(filter(lambda x: x % 2 == 0, nombres))  # [2, 4, 6]
somme  = reduce(lambda a, b: a + b, nombres, 0)   # 21

4.4. Fonctions d'ordre supérieur

Et en C ?

C possède le mécanisme — le pointeur de fonction — sans la syntaxe ni les garanties.

/* map, version C */
void appliquer(int t[], size_t n, int (*f)(int)) {
  for (size_t i = 0; i < n; i++)
    t[i] = f(t[i]);
}

int carre(int x) { return x * x; }
appliquer(t, 6, carre);

C'est l'idiome de qsort. Ce que C n'a pas : les fermetures, l'immuabilité, une valeur construite plutôt que mutée. Un paradigme n'est pas une propriété du langage : c'est une façon d'organiser le calcul, que le langage rend plus ou moins confortable.

4.5. Types algébriques et filtrage par motif

Représenter « une valeur, ou une erreur »

En C, trois réponses coexistent, et aucune n'oblige l'appelant à vérifier : une valeur sentinelle (-1, NULL), un code d'erreur, ou errno. Les types somme, eux, font de l'échec un cas du type :

// Rust : le type dit qu'il y a deux cas possibles
fn diviser(a: i32, b: i32) -> Option<i32> {
    if b == 0 { None } else { Some(a / b) }
}

match diviser(10, 0) {
    Some(v) => println!("résultat : {v}"),
    None    => println!("division par zéro"),
}

En Rust, le compilateur vérifie l'exhaustivité : oublier un cas est une erreur de compilation. Rendre les états incorrects impossibles à écrire.

Nuance : Python 3.10 offre la même syntaxe (match/case) mais aucune vérification d'exhaustivité : un match incomplet ne déclenche rien. La syntaxe est empruntée, la garantie ne l'est pas.

4. Principes des Langages de Programmation : Références

Références