Exercices - Langages et programmation¶
Constructions élémentaires¶
Exercice 1 - Identifier les constructions¶
Pour chacun des extraits Python suivants, identifier les constructions élémentaires utilisées (affectation, séquence, conditionnelle, boucle bornée, boucle non bornée, appel de fonction).
a.
b.
c.
Solution
a. Affectation (x = 10, y = x * 2), séquence (les 3 instructions dans l'ordre), appel de fonction (print(y)).
b. Boucle bornée (for i in range(5)), conditionnelle (if i % 2 == 0), appel de fonction (print(...)).
c. Appel de fonction (int(input(...))), affectation (n = ...), boucle non bornée (while n != 1), conditionnelle (if n % 2 == 0), affectation dans les branches.
Exercice 2 - Portée des variables¶
Prédire la sortie de chaque programme sans l'exécuter.
a.
x = 5
def modifier():
x = 10
print("Dans la fonction :", x)
modifier()
print("Après la fonction :", x)
b.
Solution
a.
Lex = 10 dans la fonction crée une variable locale x, distincte de la variable globale x. La variable globale x reste à 5.
b.
Le paramètrea reçoit une copie de x (3). a = a + b modifie la copie locale (a vaut 10), mais x reste à 3. La fonction renvoie 10, stocké dans z.
Exercice 3 - Comparer Python et JavaScript¶
Traduire le programme Python suivant en JavaScript :
def compter_voyelles(texte):
voyelles = "aeiouyAEIOUY"
compteur = 0
for lettre in texte:
if lettre in voyelles:
compteur = compteur + 1
return compteur
resultat = compter_voyelles("Bonjour le monde")
print(resultat)
Solution
function compterVoyelles(texte) {
let voyelles = "aeiouyAEIOUY";
let compteur = 0;
for (let i = 0; i < texte.length; i++) {
if (voyelles.includes(texte[i])) {
compteur = compteur + 1;
}
}
return compteur;
}
let resultat = compterVoyelles("Bonjour le monde");
console.log(resultat);
Points de comparaison :
- def → function, print() → console.log()
- Indentation → accolades { }
- for lettre in texte: → for (let i = 0; i < texte.length; i++)
- in (appartenance) → .includes()
Spécification et tests¶
Exercice 4 - Spécifier une fonction¶
a. Écrire la spécification complète (prototype, docstring, préconditions, postconditions) de la fonction est_premier(n) qui teste si un entier est premier.
b. Implémenter la fonction.
c. Écrire un jeu d'au moins 6 tests couvrant des cas normaux, limites et particuliers.
Solution
a. et b.
def est_premier(n: int) -> bool:
"""Détermine si n est un nombre premier.
Preconditions:
n est un entier strictement positif.
Postconditions:
Renvoie True si n est premier (divisible uniquement par 1 et lui-même).
Renvoie False sinon.
"""
assert isinstance(n, int), "n doit être un entier"
assert n > 0, "n doit être strictement positif"
if n <= 1:
return False
if n <= 3:
return True
if n % 2 == 0:
return False
d = 3
while d * d <= n:
if n % d == 0:
return False
d += 2
return True
c.
# Cas limites
assert est_premier(1) == False, "1 n'est pas premier"
assert est_premier(2) == True, "2 est premier"
assert est_premier(3) == True, "3 est premier"
# Cas normaux
assert est_premier(7) == True, "7 est premier"
assert est_premier(10) == False, "10 n'est pas premier"
assert est_premier(13) == True, "13 est premier"
# Cas particuliers
assert est_premier(4) == False, "4 n'est pas premier"
assert est_premier(97) == True, "97 est premier"
assert est_premier(100) == False, "100 n'est pas premier"
print("Tous les tests passent !")
Exercice 5 - Trouver les erreurs¶
La fonction suivante est censée renvoyer le nombre d'occurrences d'un élément dans un tableau. Elle contient des erreurs. Les trouver et les corriger.
def compter(tableau, element)
compteur = 0
for i in range(len(tableau)):
if tableau[i] = element:
compteur + 1
return compteur
Solution
Trois erreurs :
- Ligne 1 : il manque les
:après la parenthèse →def compter(tableau, element): - Ligne 4 :
=est une affectation, il faut==pour la comparaison →if tableau[i] == element: - Ligne 5 :
compteur + 1calcule mais ne stocke pas le résultat →compteur = compteur + 1(oucompteur += 1)
Version corrigée :
Exercice 6 - Tests avec doctest¶
Écrire la fonction pgcd(a, b) qui calcule le PGCD de deux entiers positifs par l'algorithme d'Euclide, avec des tests intégrés dans la docstring au format doctest.
Solution
def pgcd(a, b):
"""Calcule le PGCD de a et b par l'algorithme d'Euclide.
Preconditions: a et b entiers >= 0, pas tous les deux nuls.
>>> pgcd(12, 8)
4
>>> pgcd(7, 3)
1
>>> pgcd(100, 25)
25
>>> pgcd(17, 0)
17
>>> pgcd(0, 5)
5
>>> pgcd(48, 18)
6
"""
assert isinstance(a, int) and isinstance(b, int)
assert a >= 0 and b >= 0
assert not (a == 0 and b == 0)
while b != 0:
a, b = b, a % b
return a
if __name__ == "__main__":
import doctest
doctest.testmod()
Modules et mise au point¶
Exercice 7 - Utiliser des modules¶
a. En utilisant le module math, calculer : \(\sqrt{2}\), \(\lceil 7,3 \rceil\) (plafond), \(\lfloor 7,8 \rfloor\) (plancher), \(\log_2(256)\).
b. En utilisant le module random, écrire une fonction lancer_des(n) qui simule le lancer de n dés à 6 faces et renvoie la somme.
c. Écrire une fonction mot_de_passe(longueur) qui génère un mot de passe aléatoire de la longueur donnée, composé de lettres minuscules et de chiffres.
Solution
a.
import math
print(math.sqrt(2)) # 1.4142135623730951
print(math.ceil(7.3)) # 8
print(math.floor(7.8)) # 7
print(math.log2(256)) # 8.0
b.
import random
def lancer_des(n):
"""Simule n lancers de dé et renvoie la somme."""
somme = 0
for _ in range(n):
somme += random.randint(1, 6)
return somme
print(lancer_des(3)) # un nombre entre 3 et 18
c.
Exercice 8 - Créer un module¶
a. Créer un module geometrie.py contenant les fonctions :
aire_cercle(rayon)→ renvoie l'aire du cercle (\(\pi r^2\))perimetre_cercle(rayon)→ renvoie le périmètre (\(2 \pi r\))aire_triangle(base, hauteur)→ renvoie l'aire (\(\frac{base \times hauteur}{2}\))
b. Ajouter des assertions de précondition (rayon, base et hauteur > 0).
c. Ajouter un bloc if __name__ == "__main__" avec des tests.
d. Utiliser ce module depuis un fichier programme.py.
Solution
geometrie.py :
import math
def aire_cercle(rayon):
"""Renvoie l'aire d'un cercle de rayon donné."""
assert rayon > 0, "Le rayon doit être strictement positif"
return math.pi * rayon ** 2
def perimetre_cercle(rayon):
"""Renvoie le périmètre d'un cercle de rayon donné."""
assert rayon > 0, "Le rayon doit être strictement positif"
return 2 * math.pi * rayon
def aire_triangle(base, hauteur):
"""Renvoie l'aire d'un triangle."""
assert base > 0 and hauteur > 0, "Base et hauteur doivent être positives"
return base * hauteur / 2
if __name__ == "__main__":
assert abs(aire_cercle(1) - math.pi) < 0.001
assert abs(perimetre_cercle(1) - 2 * math.pi) < 0.001
assert aire_triangle(6, 4) == 12.0
print("Tests OK !")
programme.py :
Exercice 9 - Déboguer un programme¶
Le programme suivant est censé renvoyer la liste des nombres premiers inférieurs à n. Il contient une erreur de logique. La trouver en utilisant des affichages intermédiaires.
def premiers_jusqu_a(n):
resultat = []
for candidat in range(2, n):
est_premier = True
for diviseur in range(2, candidat):
if candidat % diviseur == 0:
est_premier = False
if est_premier:
resultat.append(candidat)
return resultat
print(premiers_jusqu_a(20))
a. Exécuter le programme. Le résultat est-il correct ?
b. Si non, ajouter des print() pour identifier le problème et corriger.
Solution
a. Le programme fonctionne correctement et renvoie [2, 3, 5, 7, 11, 13, 17, 19].
Cependant, il est inefficace : même après avoir trouvé un diviseur, la boucle interne continue. On peut l'optimiser avec un break :
b.
def premiers_jusqu_a(n):
resultat = []
for candidat in range(2, n):
est_premier = True
for diviseur in range(2, candidat):
if candidat % diviseur == 0:
est_premier = False
break # inutile de continuer, on sait que c'est pas premier
if est_premier:
resultat.append(candidat)
return resultat
On peut aussi optimiser en ne testant que jusqu'à \(\sqrt{candidat}\) :
Exercice 10 - Messages d'erreur¶
Pour chaque extrait de code, prédire le type d'erreur Python qui sera levée et expliquer pourquoi.
a. print(10 / 0)
b. int("abc")
c. [1, 2, 3][5]
d. "3" + 5
e. {"a": 1}["b"]
Solution
a. ZeroDivisionError : division par zéro, opération mathématiquement interdite.
b. ValueError : la chaîne "abc" ne peut pas être convertie en entier.
c. IndexError: list index out of range : l'indice 5 n'existe pas dans une liste de 3 éléments (indices 0, 1, 2).
d. TypeError: can only concatenate str to str : on ne peut pas additionner une chaîne et un entier. Il faudrait écrire "3" + str(5) ou int("3") + 5.
e. KeyError: 'b' : la clé "b" n'existe pas dans le dictionnaire. On peut utiliser .get("b", valeur_defaut) pour éviter l'erreur.
Activités¶
🧪 Activité 1 - Le même algorithme en plusieurs langages¶
Objectif
Comparer l'implémentation d'un même algorithme dans différents langages pour identifier les traits communs et les différences.
Étapes¶
a. Écrire une fonction qui calcule le maximum d'un tableau en Python, JavaScript et pseudo-code.
b. Pour chaque version, identifier : la déclaration de fonction, la boucle, la condition, le retour de valeur.
c. Quels sont les traits communs entre ces langages ? Quels sont les traits particuliers à chaque langage ?
Correction
Python :
def maximum(tableau):
maxi = tableau[0]
for element in tableau:
if element > maxi:
maxi = element
return maxi
JavaScript :
function maximum(tableau) {
let maxi = tableau[0];
for (let i = 0; i < tableau.length; i++) {
if (tableau[i] > maxi) {
maxi = tableau[i];
}
}
return maxi;
}
Traits communs : mot-clé return, variable maxi, comparaison >, structure if.
Différences : Python utilise for element in tableau (itération directe) et l'indentation, JavaScript utilise for (let i=0; ...) (itération par indice) et les accolades.
🧪 Activité 2 - Atelier TDD¶
Objectif
Pratiquer le développement piloté par les tests en écrivant les tests avant le code.
Étapes¶
On souhaite écrire une fonction cesar(texte, decalage) qui chiffre un texte par le chiffre de César (décalage de chaque lettre dans l'alphabet).
a. Écrire d'abord les tests :
assert cesar("ABC", 3) == "DEF"
assert cesar("XYZ", 3) == "ABC"
assert cesar("HELLO", 0) == "HELLO"
assert cesar("NSI", 13) == "AFV"
assert cesar("abc", 1) == "bcd"
b. Implémenter la fonction pour que tous les tests passent. Les caractères non alphabétiques restent inchangés.
c. Écrire la fonction de déchiffrement decesar(texte, decalage) et vérifier que decesar(cesar(texte, d), d) == texte.
Correction
def cesar(texte, decalage):
"""Chiffre un texte par le chiffre de César."""
resultat = ""
for c in texte:
if "A" <= c <= "Z":
resultat += chr((ord(c) - ord("A") + decalage) % 26 + ord("A"))
elif "a" <= c <= "z":
resultat += chr((ord(c) - ord("a") + decalage) % 26 + ord("a"))
else:
resultat += c
return resultat
def decesar(texte, decalage):
"""Déchiffre un texte chiffré par César."""
return cesar(texte, -decalage)
# Tests
assert cesar("ABC", 3) == "DEF"
assert cesar("XYZ", 3) == "ABC"
assert cesar("HELLO", 0) == "HELLO"
assert cesar("NSI", 13) == "AFV"
assert cesar("abc", 1) == "bcd"
# Test aller-retour
assert decesar(cesar("Bonjour le monde", 7), 7) == "Bonjour le monde"
print("Tous les tests passent !")
🎯 Projet final - Bibliothèque de fonctions utilitaires¶
Objectif
Créer un module Python complet, spécifié, testé et documenté.
Partie A - Créer le module¶
a. Créer un fichier utilitaires.py contenant les fonctions suivantes, chacune avec docstring, préconditions et postconditions :
inverser(texte): renvoie le texte inverséest_palindrome(texte): vérifie si un texte est un palindrome (ignore la casse)compter_mots(texte): renvoie le nombre de mots dans un textefrequence_lettres(texte): renvoie un dictionnaire des fréquences de chaque lettre
Partie B - Tester¶
b. Écrire un jeu de tests complet pour chaque fonction (au moins 4 tests par fonction, incluant les cas limites).
c. Ajouter les tests dans la docstring au format doctest.
Partie C - Utiliser¶
d. Créer un fichier analyse.py qui importe le module et analyse un texte saisi par l'utilisateur : affiche le texte inversé, teste s'il est palindrome, compte les mots et affiche les 5 lettres les plus fréquentes.
Correction
utilitaires.py :
def inverser(texte):
"""Renvoie le texte inversé.
>>> inverser("Bonjour")
'ruojnoB'
>>> inverser("")
''
>>> inverser("a")
'a'
>>> inverser("abba")
'abba'
"""
return texte[::-1]
def est_palindrome(texte):
"""Vérifie si texte est un palindrome (insensible à la casse).
>>> est_palindrome("kayak")
True
>>> est_palindrome("Kayak")
True
>>> est_palindrome("python")
False
>>> est_palindrome("")
True
>>> est_palindrome("a")
True
"""
texte_min = texte.lower()
return texte_min == texte_min[::-1]
def compter_mots(texte):
"""Renvoie le nombre de mots dans le texte.
>>> compter_mots("Bonjour le monde")
3
>>> compter_mots("")
0
>>> compter_mots("mot")
1
>>> compter_mots(" espaces multiples ")
2
"""
if texte.strip() == "":
return 0
return len(texte.split())
def frequence_lettres(texte):
"""Renvoie un dictionnaire des fréquences de chaque lettre (en minuscule).
>>> frequence_lettres("aab")
{'a': 2, 'b': 1}
>>> frequence_lettres("")
{}
>>> frequence_lettres("123")
{}
"""
freq = {}
for c in texte.lower():
if c.isalpha():
if c in freq:
freq[c] += 1
else:
freq[c] = 1
return freq
if __name__ == "__main__":
import doctest
doctest.testmod()
print("Tous les tests passent !")
analyse.py :
from utilitaires import inverser, est_palindrome, compter_mots, frequence_lettres
texte = input("Entrez un texte : ")
print(f"\nTexte inversé : {inverser(texte)}")
print(f"Palindrome : {'Oui' if est_palindrome(texte) else 'Non'}")
print(f"Nombre de mots : {compter_mots(texte)}")
freq = frequence_lettres(texte)
top5 = sorted(freq.items(), key=lambda x: x[1], reverse=True)[:5]
print("Top 5 des lettres :")
for lettre, nombre in top5:
print(f" '{lettre}' : {nombre} fois")