Pour chaque question, une seule réponse est correcte.
1. Parmi les caractéristiques suivantes, laquelle est propre au paradigme fonctionnel ?
for et whileCorrection publiée après la séance.
2. Quel est le résultat de list(map(lambda x: x ** 2, [1, 2, 3])) ?
[2, 4, 6][1, 8, 27][1, 2, 3][1, 4, 9]Correction publiée après la séance.
3. Qu’est-ce qu’une fonction d’ordre supérieur ?
Correction publiée après la séance.
4. Quelle expression Python est écrite en style fonctionnel ?
result = 0 ; for x in L: result += xwhile i < len(L): i += 1sum(filter(lambda x: x > 0, L))obj.calculer(L)Correction publiée après la séance.
Pour chaque extrait de code, indiquez s’il est écrit en style impératif, fonctionnel ou objet. Justifiez.
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.
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']
map, filter et lambda (exercice guidé) (suite)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] :
[9, 49, 4, 81, 16] ;[2, 4] ;[7, 9].Donnez chaque réponse avec map/filter + lambda, puis avec une compréhension de liste.
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].
La fonction len s’applique à chaque mot : map peut la recevoir directement.
map, filter et lambda (exercice guidé) (suite)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).
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.
sorted avec key et lambdaa) Trier la liste mots = ['Python', 'est', 'un', 'langage', 'multiparadigme'] par longueur croissante.
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.
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.
sorted avec key et lambda (suite)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.
Réécrivez chaque fonction en style fonctionnel (pas de variable modifiable, pas de boucle, récursivité uniquement).
a) Longueur d’une liste :
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 :
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 :
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.
Pour chaque fonction, indiquez si elle est pure ou impure. Justifiez.
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.
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
forall (exercice guidé) (suite)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.
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 ?
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 ?
map(pred, L) donne, pour chaque élément, True ou False ; all renvoie True si toutes ces valeurs sont vraies.
forall (exercice guidé) (suite)d) Écrivez forall avec une compréhension de liste et all.
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.
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.
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).
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.
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.
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).
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.
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 ?
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.