10 - Fonctions (3)
Prérequis : ce TD suppose acquises les notions de fonctions (paramètres,
return, docstring), de bouclesforet de manipulation de listes (accès par indice,append). Il mobilise également les tuples pour renvoyer plusieurs valeurs.
Exercice 1 : QCM – vérification des prérequis
Pour chaque question, une seule réponse est correcte.
1. Que renvoie la fonction suivante pour f([3, 1, 4]) ?
def f(L):
r = L[0]
for x in L:
if x > r:
r = x
return r
- A.
3 - B.
4 - C.
1 - D.
[3, 1, 4]
Correction
Réponse : B. La fonction parcourt la liste et conserve dans r le plus grand élément rencontré. En partant de r = 3, elle compare avec 1 (pas de changement), puis 4 > 3 donc r = 4.
- A est faux : c’est la valeur initiale de
r, mais4 > 3la remplace. - C est faux :
1est le minimum, pas le maximum. - D est faux : la fonction renvoie un entier, pas une liste.
2. Que renvoie (3, 7)[1] ?
- A.
3 - B.
7 - C.
(3, 7) - D. Erreur
Correction
Réponse : B. Un tuple s’indexe comme une liste. (3, 7)[1] accède à l’élément d’indice 1, soit 7.
- A est faux : c’est l’élément d’indice 0.
- C est faux : on accède à un élément, pas au tuple entier.
- D est faux : l’indexation d’un tuple est valide.
3. On souhaite renvoyer à la fois le minimum et le maximum d’une liste. Quelle instruction return est correcte ?
- A.
return min, return max - B.
return (min, max) - C.
return min et max - D.
print(min, max)
Correction
Réponse : B. Pour renvoyer deux valeurs, on les emballe dans un tuple : return (min, max) (les parenthèses sont facultatives : return min, max fonctionne aussi).
- A est faux : on ne peut pas écrire deux
returnsur la même ligne avec une virgule. - C est faux :
etest l’opérateur logiqueand, pas une façon de renvoyer deux valeurs. - D est faux :
printaffiche mais ne renvoie rien d’exploitable (la fonction renverraitNone).
4. Que vaut n après l’exécution du code suivant ?
n = 0
for i in range(len([10, 20, 30, 40])):
n = n + 1
- A.
3 - B.
4 - C.
100 - D.
10
Correction
Réponse : B. len([10, 20, 30, 40]) vaut 4, donc range(4) produit 0, 1, 2, 3 (quatre itérations). Le compteur n est incrémenté quatre fois.
- A est faux : erreur du « off-by-one ».
- C est faux : confusion entre
n + 1(incrémentation du compteur) et la somme des éléments. - D est faux : c’est le premier élément de la liste, pas le nombre d’itérations.
Exercice 2 : exemple travaillé – trouver le maximum avec sa position
Problème : écrire une fonction qui renvoie le maximum d’une liste et la position de sa première apparition, sans utiliser max() ni index().
Étape 1. Stratégie : On parcourt la liste en gardant en mémoire deux informations : la plus grande valeur rencontrée et son indice.
Étape 2. Initialisation : On initialise avec le premier élément (indice 0). Ainsi, on est sûr de partir d’une valeur réelle de la liste.
Étape 3. Parcours et comparaison :
def pos_max(liste):
"""Renvoie un tuple (maximum, indice) de la première
apparition du maximum dans la liste.
>>> pos_max([2, 7, 1, 8, 5])
(8, 3)
>>> pos_max([5, 5, 5])
(5, 0)
"""
valeur_max = liste[0]
indice_max = 0
for i in range(1, len(liste)):
if liste[i] > valeur_max:
valeur_max = liste[i]
indice_max = i
return (valeur_max, indice_max)
Trace pour pos_max([2, 7, 1, 8, 5]) :
| Tour | i | liste[i] | Comparaison | valeur_max | indice_max |
|---|---|---|---|---|---|
| init | — | — | — | 2 | 0 |
| 1 | 1 | 7 | 7 > 2 ✓ | 7 | 1 |
| 2 | 2 | 1 | 1 > 7 ✗ | 7 | 1 |
| 3 | 3 | 8 | 8 > 7 ✓ | 8 | 3 |
| 4 | 4 | 5 | 5 > 8 ✗ | 8 | 3 |
Résultat : (8, 3) ✓
Points clés :
- On commence la boucle à l’indice 1 (pas 0) car l’indice 0 sert d’initialisation.
- Le test
>(strict) garantit de garder la première apparition du maximum. - On renvoie un tuple pour empaqueter les deux informations.
Exercice 3 : minimum et maximum
Écrire une fonction min_et_max(liste) qui prend en paramètre une liste non vide d’entiers et retourne un couple formé du minimum et du maximum de la liste. Vous ne pouvez utiliser ni min, ni max.
print(min_et_max([2, 7, 1, 8, 5]))
# (1, 8)
Indication : adapter le schéma de l’exemple travaillé en maintenant deux variables au lieu d’une.
Correction
def min_et_max(liste):
"""Renvoie un tuple (minimum, maximum) de la liste.
>>> min_et_max([2, 7, 1, 8, 5])
(1, 8)
>>> min_et_max([42])
(42, 42)
"""
mini = liste[0]
maxi = liste[0]
for i in range(1, len(liste)):
if liste[i] < mini:
mini = liste[i]
if liste[i] > maxi:
maxi = liste[i]
return (mini, maxi)
# Tests
assert min_et_max([2, 7, 1, 8, 5]) == (1, 8)
assert min_et_max([42]) == (42, 42)
assert min_et_max([5, 3, 5, 3]) == (3, 5)
print("Tests OK")
Attention : les deux tests if ne sont pas liés par un elif car un élément pourrait être à la fois le nouveau minimum et le nouveau maximum (cas d’une liste à un seul élément à l’initialisation).
Exercice 4 : positions du minimum et du maximum
Écrire une fonction pos_min_et_max(liste) qui renvoie un couple formé des positions (indices) de la première apparition du minimum et du maximum. Vous ne pouvez utiliser ni min, ni max, ni index.
print(pos_min_et_max([2, 7, 1, 8, 5]))
# (2, 3)
Correction
def pos_min_et_max(liste):
"""Renvoie un tuple (indice_min, indice_max).
>>> pos_min_et_max([2, 7, 1, 8, 5])
(2, 3)
>>> pos_min_et_max([5, 5, 5])
(0, 0)
"""
i_min = 0
i_max = 0
for i in range(1, len(liste)):
if liste[i] < liste[i_min]:
i_min = i
if liste[i] > liste[i_max]:
i_max = i
return (i_min, i_max)
assert pos_min_et_max([2, 7, 1, 8, 5]) == (2, 3)
assert pos_min_et_max([5, 5, 5]) == (0, 0)
assert pos_min_et_max([1]) == (0, 0)
print("Tests OK")
Astuce : on stocke les indices (pas les valeurs) et on compare via liste[i_min] et liste[i_max]. Le test strict < et > garantit que la première apparition est conservée.
Exercice 5 : toutes les positions du maximum
Écrire une fonction liste_pos_max(liste) qui renvoie un couple formé du maximum et de la liste de toutes les positions où il apparaît. Vous ne pouvez utiliser ni max, ni index.
print(liste_pos_max([2, 5, 1, 5, 5]))
# (5, [1, 3, 4])
Indication : procéder en deux étapes : trouver le maximum, puis collecter tous ses indices.
Correction
def liste_pos_max(liste):
"""Renvoie (maximum, liste_des_positions).
>>> liste_pos_max([2, 5, 1, 5, 5])
(5, [1, 3, 4])
>>> liste_pos_max([3, 3, 3])
(3, [0, 1, 2])
"""
# Étape 1 : trouver le maximum
maxi = liste[0]
for x in liste:
if x > maxi:
maxi = x
# Étape 2 : collecter les positions
positions = []
for i in range(len(liste)):
if liste[i] == maxi:
positions.append(i)
return (maxi, positions)
assert liste_pos_max([2, 5, 1, 5, 5]) == (5, [1, 3, 4])
assert liste_pos_max([3, 3, 3]) == (3, [0, 1, 2])
assert liste_pos_max([1, 2, 3]) == (3, [2])
print("Tests OK")
Variante en un seul parcours (plus difficile) :
def liste_pos_max(liste):
maxi = liste[0]
positions = [0]
for i in range(1, len(liste)):
if liste[i] > maxi:
maxi = liste[i]
positions = [i] # nouveau max, on réinitialise
elif liste[i] == maxi:
positions.append(i) # même max, on ajoute
return (maxi, positions)
La variante en un seul parcours est plus efficace mais aussi plus subtile : quand on trouve un nouveau maximum, on réinitialise la liste des positions.
Exercice 6 : bataille simplifiée
Écrire une fonction bataille_simple(liste1, liste2) qui prend deux listes de même longueur et renvoie un couple (score1, score2). Le score d’une liste est le nombre de positions où son élément est strictement plus grand que celui de l’autre liste.
print(bataille_simple([1, 2, 3, 3], [4, 2, 5, 0]))
# (1, 2)
Correction
def bataille_simple(liste1, liste2):
"""Compare deux listes position par position.
>>> bataille_simple([1, 2, 3, 3], [4, 2, 5, 0])
(1, 2)
>>> bataille_simple([1, 1, 1], [1, 1, 1])
(0, 0)
"""
score1 = 0
score2 = 0
for i in range(len(liste1)):
if liste1[i] > liste2[i]:
score1 += 1
elif liste1[i] < liste2[i]:
score2 += 1
# si égalité, aucun score ne change
return (score1, score2)
assert bataille_simple([1, 2, 3, 3], [4, 2, 5, 0]) == (1, 2)
assert bataille_simple([1, 1, 1], [1, 1, 1]) == (0, 0)
print("Tests OK")
Trace pour ([1, 2, 3, 3], [4, 2, 5, 0]) :
i | liste1[i] | liste2[i] | Résultat | score1 | score2 |
|---|---|---|---|---|---|
| 0 | 1 | 4 | 1 < 4 | 0 | 1 |
| 1 | 2 | 2 | égalité | 0 | 1 |
| 2 | 3 | 5 | 3 < 5 | 0 | 2 |
| 3 | 3 | 0 | 3 > 0 | 1 | 2 |
Exercice 7 : fusion min/max de deux listes
Écrire une fonction liste_min_max(liste1, liste2) qui prend deux listes de même longueur et renvoie deux listes : la première contient à chaque position le plus petit des deux éléments, la seconde le plus grand.
print(liste_min_max([1, 2, 3], [4, -1, 5]))
# ([1, -1, 3], [4, 2, 5])
Correction
def liste_min_max(liste1, liste2):
"""Renvoie (liste_des_min, liste_des_max).
>>> liste_min_max([1, 2, 3], [4, -1, 5])
([1, -1, 3], [4, 2, 5])
"""
mins = []
maxs = []
for i in range(len(liste1)):
if liste1[i] <= liste2[i]:
mins.append(liste1[i])
maxs.append(liste2[i])
else:
mins.append(liste2[i])
maxs.append(liste1[i])
return (mins, maxs)
assert liste_min_max([1, 2, 3], [4, -1, 5]) == ([1, -1, 3], [4, 2, 5])
assert liste_min_max([5, 5], [5, 5]) == ([5, 5], [5, 5])
print("Tests OK")
Exercice 8 : synthèse – nombres narcissiques
On dit qu’un nombre de $n$ chiffres est narcissique si la somme de chacun de ses chiffres élevé à la puissance $n$ est égale au nombre lui-même. Par exemple :
$$153 = 1^3 + 5^3 + 3^3$$
$$93,084 = 9^5 + 3^5 + 0^5 + 8^5 + 4^5$$
Écrire une fonction narcissique(nbre) qui renvoie True ou False selon que le nombre nbre est narcissique ou non. Vous ne pouvez utiliser ni liste en compréhension, ni conversion int vers str, ni la fonction sum. Vous pouvez utiliser deux boucles.
Indication : procéder en deux étapes :
- Compter le nombre de chiffres $n$ en divisant successivement par 10.
- Extraire chaque chiffre (avec
% 10et// 10) et calculer la somme des puissances.
print(narcissique(153)) # True
print(narcissique(370)) # True
print(narcissique(100)) # False
Correction
def narcissique(nbre):
"""Vérifie si nbre est un nombre narcissique.
>>> narcissique(153)
True
>>> narcissique(370)
True
>>> narcissique(9)
True
>>> narcissique(100)
False
"""
# Étape 1 : compter le nombre de chiffres
n = 0
temp = nbre
while temp > 0:
temp = temp // 10
n = n + 1
# Étape 2 : calculer la somme des puissances
somme = 0
temp = nbre
while temp > 0:
chiffre = temp % 10 # dernier chiffre
somme = somme + chiffre ** n
temp = temp // 10 # on retire le dernier chiffre
return somme == nbre
# Tests
assert narcissique(153) == True
assert narcissique(370) == True
assert narcissique(371) == True
assert narcissique(407) == True
assert narcissique(9) == True
assert narcissique(100) == False
assert narcissique(10) == False
print("Tests OK")
Trace pour narcissique(153) :
Étape 1 – comptage :
temp | temp // 10 | n |
|---|---|---|
| 153 | 15 | 1 |
| 15 | 1 | 2 |
| 1 | 0 | 3 |
→ $n = 3$ chiffres.
Étape 2 – somme des puissances :
temp | chiffre | chiffre ** 3 | somme |
|---|---|---|---|
| 153 | 3 | 27 | 27 |
| 15 | 5 | 125 | 152 |
| 1 | 1 | 1 | 153 |
→ somme == nbre → 153 == 153 → True ✓