Exercice 1 : QCM
Pour chaque question, une seule réponse est correcte.
1. Parmi les caractéristiques suivantes, laquelle est propre au paradigme fonctionnel ?
- A. L’utilisation de boucles
foretwhile - B. L’encapsulation de données dans des objets
- C. La composition de fonctions et l’absence d’effets de bord
- D. La modification de variables globales
Correction publiée après la séance.
2. Quel est le résultat de list(map(lambda x: x ** 2, [1, 2, 3])) ?
- A.
[2, 4, 6] - B.
[1, 8, 27] - C.
[1, 2, 3] - D.
[1, 4, 9]
Correction publiée après la séance.
3. Qu’est-ce qu’une fonction d’ordre supérieur ?
- A. Une fonction qui prend une autre fonction en paramètre ou qui renvoie une fonction
- B. Une fonction qui s’appelle elle-même
- C. Une fonction définie à l’intérieur d’une autre fonction
- D. Une fonction qui modifie une variable globale
Correction publiée après la séance.
4. Quelle expression Python est écrite en style fonctionnel ?
- A.
result = 0 ; for x in L: result += x - B.
while i < len(L): i += 1 - C.
sum(filter(lambda x: x > 0, L)) - D.
obj.calculer(L)
Correction publiée après la séance.
Exercice 2 : reconnaître le paradigme
Pour chaque extrait de code, indiquez s’il est écrit en style impératif, fonctionnel ou objet. Justifiez.
Indice
Impératif : des instructions qui modifient des variables, souvent dans des boucles. Fonctionnel : des fonctions sans effet de bord, la récursivité, map, filter, lambda. Objet : une classe dont les méthodes manipulent les attributs.
a)
def somme(L):
total = 0
for x in L:
total += x
return total
b)
def somme(L):
return 0 if L == [] else L[0] + somme(L[1:])
c)
class Calcul:
def __init__(self, L):
self.L = L
def somme(self):
return sum(self.L)
d)
from functools import reduce
somme = lambda L: reduce(lambda acc, x: acc + x, L, 0)
Correction publiée après la séance.
Exercice 3 : map, filter et lambda (exercice guidé)
a) Complétez les expressions suivantes (notées ...) :
L = [3, 7, 2, 9, 4]
# Doubler chaque élément
list(map(lambda x: ..., L)) # → [6, 14, 4, 18, 8]
# Garder seulement les éléments impairs
list(filter(lambda x: ..., L)) # → [3, 7, 9]
# Convertir en chaînes
list(map(..., L)) # → ['3', '7', '2', '9', '4']
Indice
map(f, L) applique f à chaque élément ; filter(p, L) garde les éléments pour lesquels p renvoie True. Pour convertir en chaîne, la fonction str se passe directement à map, sans lambda.
b) Écrivez les expressions pour obtenir, à partir de L = [3, 7, 2, 9, 4] :
- la liste des carrés :
[9, 49, 4, 81, 16]; - la liste des éléments pairs :
[2, 4]; - la liste des éléments supérieurs à 5 :
[7, 9].
Donnez chaque réponse avec map/filter + lambda, puis avec une compréhension de liste.
Indice
Une transformation de chaque élément s’écrit avec map, une sélection avec filter. En compréhension de liste, la transformation se place avant for, la condition après if.
c) À partir de mots = ['Python', 'est', 'un', 'langage'], obtenir la liste des longueurs : [6, 3, 2, 7].
Indice
La fonction len s’applique à chaque mot : map peut la recevoir directement.
d) À partir de L = [-3, 5, -1, 8, -2, 7], obtenir la liste des valeurs absolues des éléments négatifs : [3, 1, 2] (combinaison de filter et map).
Indice
D’abord filter pour garder les éléments négatifs, puis map avec abs sur le résultat : list(map(abs, filter(..., L))).
Correction publiée après la séance.
Exercice 4 : sorted avec key et lambda
a) Trier la liste mots = ['Python', 'est', 'un', 'langage', 'multiparadigme'] par longueur croissante.
Indice
Le paramètre key de sorted reçoit une fonction qui donne, pour chaque élément, la valeur à comparer : quelle fonction donne la longueur d’un mot ?
b) Trier la liste eleves = [('Alice', 15), ('Bob', 12), ('Clara', 18), ('David', 14)] par note décroissante.
Indice
La clé est la note, deuxième élément du tuple : lambda e: e[1]. Pour l’ordre décroissant, ajouter reverse=True.
c) Trier la liste points = [(1, 5), (3, 2), (0, 0), (4, 1)] par distance croissante à l’origine.
Indice
La clé peut être le carré de la distance, x ** 2 + y ** 2 : il range les points dans le même ordre que la distance, sans racine carrée.
Correction publiée après la séance.
Exercice 5 : réécriture impératif vers fonctionnel
Réécrivez chaque fonction en style fonctionnel (pas de variable modifiable, pas de boucle, récursivité uniquement).
a) Longueur d’une liste :
Indice
Cas de base : la liste vide a pour longueur 0. Sinon, la longueur vaut 1 plus la longueur de la liste privée de son premier élément, L[1:].
def longueur(L):
compteur = 0
for _ in L:
compteur += 1
return compteur
b) Appartenance d’un élément à une liste :
Indice
Une liste vide ne contient pas x. Sinon, x est dans L s’il est égal à L[0] ou s’il appartient à L[1:].
def appartient(x, L):
for elem in L:
if elem == x:
return True
return False
c) Nombre d’occurrences :
Indice
Liste vide : 0 occurrence. Sinon, compter 1 si L[0] vaut x et 0 sinon, puis ajouter le nombre d’occurrences dans L[1:].
def nb_occurrences(x, L):
compteur = 0
for elem in L:
if elem == x:
compteur += 1
return compteur
Correction publiée après la séance.
Exercice 6 : fonctions pures et effets de bord
Pour chaque fonction, indiquez si elle est pure ou impure. Justifiez.
Indice
Une fonction est pure si son résultat ne dépend que de ses paramètres et si elle ne modifie rien en dehors d’elle : ni variable globale, ni liste reçue en paramètre.
a)
def double(x):
return 2 * x
b)
compteur = 0
def incrementer():
global compteur
compteur += 1
return compteur
c)
def ajouter(x, L):
L.append(x)
return L
d)
def ajouter_pur(x, L):
return L + [x]
Correction publiée après la séance.
Exercice 7 : la fonction forall (exercice guidé)
On souhaite écrire une fonction forall(pred, L) qui renvoie True si tous les éléments de L vérifient le prédicat pred.
Données de test :
pair = lambda x: x % 2 == 0
ex1 = [4, 4, 2, 0, 1] # attendu : False
ex2 = [] # attendu : True
ex3 = [4, 4, 2, 0, 8] # attendu : True
a) Écrivez forall avec une boucle for. C’est la version la plus naturelle : dès qu’un élément ne vérifie pas le prédicat, on renvoie False.
Indice
Parcourir L : si pred(x) est faux pour un élément, renvoyer False. Après la boucle, tous les éléments conviennent : renvoyer True.
b) Écrivez forall en version récursive pure (sans boucle, sans variable). Quel est le cas de base ? Comment se réduit le problème ?
Indice
Cas de base : la liste vide, pour laquelle la réponse est True, car aucun élément ne contredit le prédicat. Sinon, tous les éléments conviennent si le premier convient et si tous ceux de L[1:] conviennent.
c) Écrivez forall avec map et all. Quel est le rôle de chacune de ces fonctions ?
Indice
map(pred, L) donne, pour chaque élément, True ou False ; all renvoie True si toutes ces valeurs sont vraies.
d) Écrivez forall avec une compréhension de liste et all.
Indice
La compréhension [pred(x) for x in L] donne la liste des réponses du prédicat ; il reste à la passer à all.
e) Écrivez forall avec filter et len.
Indice
filter(pred, L) garde les éléments qui vérifient le prédicat : tous le vérifient si ce filtrage en garde autant que L en contient.
Correction publiée après la séance.
Exercice 8 : synthèse – trois paradigmes sur un même problème
On dispose d’une liste d’enregistrements représentant des élèves :
eleves = [
{"nom": "Alice", "notes": [15, 12, 18]},
{"nom": "Bob", "notes": [8, 14, 9]},
{"nom": "Clara", "notes": [16, 17, 19]},
{"nom": "David", "notes": [11, 7, 13]},
]
On souhaite obtenir la liste des noms des élèves dont la moyenne est supérieure ou égale à 14.
Résultat attendu : ['Alice', 'Clara']
a) Écrire une solution en style impératif (boucle, variable accumulatrice).
Indice
Parcourir les élèves : calculer la moyenne de eleve["notes"] avec sum et len, et ajouter eleve["nom"] à la liste résultat si cette moyenne vaut au moins 14.
Code à copier puis compléter
resultat = []
for eleve in eleves:
moyenne = sum(eleve["notes"]) / len(eleve["notes"])
if ...:
resultat.append(...)
print(resultat)
b) Écrire une solution en style fonctionnel (avec filter, map, lambda), puis en compréhension de liste.
Indice
filter garde les élèves dont la moyenne vaut au moins 14, puis map extrait leur nom avec lambda e: e["nom"]. En compréhension : [e["nom"] for e in eleves if ...].
c) Écrire une solution en style objet (avec une classe Eleve disposant des méthodes moyenne et est_bon).
Indice
Le constructeur range nom et notes dans des attributs ; moyenne calcule la moyenne de self.notes, et est_bon compare cette moyenne au seuil. Créer ensuite un objet Eleve pour chaque dictionnaire de la liste.
Code à copier puis compléter
class Eleve:
def __init__(self, nom, notes):
self.nom = nom
self.notes = ...
def moyenne(self):
return sum(self.notes) / ...
def est_bon(self, seuil=14):
return self.moyenne() >= ...
d) Comparer les trois approches : laquelle est la plus lisible ? La plus courte ? La plus facilement testable ?
Indice
Comparer le nombre de lignes, la présence de variables modifiées, et la facilité d’appeler chaque partie séparément pour la tester.
Correction publiée après la séance.