Files
2026_TD2_DEV51_Qualite_Algo/COMPTE_RENDU.md
T

7.8 KiB
Raw Blame History

Compte rendu – TD Complexité cyclomatique (R5.A.04)

Partie 1 – Graphes de flot de contrôle

Pour chaque fonction demandée dans l'énnoncé, on construit le graphe de flot de contrôle, puis on applique :

M = E − N + 2P (E = arêtes, N = nœuds, P = 1 pour une fonction)

On vérifie ensuite le résultat avec la règle pratique M = décisions + 1.

C2 – moyenne (C)

Graphe C2

Nœud Contenu
1 somme = 0 ; i = 0
2 décision i < n
3 somme += t[i]
4 i++ (arête de retour vers 2)
5 décision n > 0 (ternaire)
6 somme / n
7 0
8 sortie (return)
  • N = 8, E = 9, P = 1
  • M = 9 − 8 + 2 × 1 = 3
  • Vérification : 1 for + 1 ternaire ?: + 1 = 3

C4 – nom_jour (C)

Graphe C4

Nœud Contenu
1 switch (jour)
2 à 8 case 1 à case 7 : return "lundi" … return "dimanche"
9 default : return "inconnu"
10 sortie
  • N = 10, E = 16 (8 arêtes du switch vers chaque branche + 8 arêtes des branches vers la sortie), P = 1
  • M = 16 − 10 + 2 × 1 = 8
  • Vérification : 7 case + 1 = 8 (le default ne compte pas : c'est le « sinon » du switch)

J2 – estDisponible (Java)

Graphe J2

La méthode ne contient aucun if, mais les && sont évalués paresseusement : chaque opérande est une décision (si livre.emprunte est vrai, on sort directement sans évaluer la suite).

Nœud Contenu
1 décision !livre.emprunte
2 décision !livre.reserve
3 décision !livre.abime
4 sortie (return)
  • N = 4, E = 5, P = 1
  • M = 5 − 4 + 2 × 1 = 3
  • Vérification : 2 opérateurs && + 1 = 3

J6 – importer (Java)

Graphe J6

Les nœuds de décision sont en jaune, et la condition de la boucle for est en bleu.

Nœud Contenu
1 importes = 0
2 décision for : reste-t-il une ligne ?
3 décision ligne.isBlank()
4 décision ligne.startsWith("#") (le ||)
5 continue
6 champs = ligne.split(";")
7 décision champs.length != 4
8 System.err.println(...) + continue
9 try : annee = Integer.parseInt(...)
10 décision annee > 0
11 décision annee <= 2026 (le &&)
12 livres.add(...) + importes++
13 décision catch : NumberFormatException levée ?
14 System.err.println("Année invalide ...")
15 sortie (return importes)

Arêtes : 1→2, 2→3, 2→15, 3→4, 3→5, 4→5, 4→6, 5→2, 6→7, 7→8, 7→9, 8→2, 9→10, 10→11, 10→13, 11→12, 11→13, 12→13, 13→2, 13→14, 14→2.

  • N = 15, E = 21, P = 1
  • M = 21 − 15 + 2 × 1 = 8
  • Vérification : 1 for + 1 if + 1 || + 1 if + 1 if + 1 && + 1 catch = 7 décisions, + 1 = 8

Cas particuliers : le || et le && comptent chacun comme une décision (évaluation paresseuse), et le catch aussi, puisqu'il crée un second chemin de sortie du try. Les continue ne comptent pas : ils renvoient simplement vers la condition de la boucle.

P2 – signe (Python)

Graphe P2

Nœud Contenu
1 décision x > 0
2 return 1
3 décision x < 0 (elif)
4 return -1
5 return 0
6 sortie
  • N = 6, E = 7, P = 1
  • M = 7 − 6 + 2 × 1 = 3
  • Vérification : 1 if + 1 elif + 1 = 3

P3 – notes_valides (Python)

Graphe P3

La compréhension de liste cache une boucle et un filtre : elle se lit comme un for contenant un if.

Nœud Contenu
1 initialisation de la liste résultat
2 décision : reste-t-il un élément n dans notes ?
3 décision 0 <= n <= 20
4 ajout de n au résultat (retour vers 2)
5 sortie (return)
  • N = 5, E = 6, P = 1
  • M = 6 − 5 + 2 × 1 = 3
  • Vérification : 1 for + 1 if (dans la compréhension) + 1 = 3

Remarque : 0 <= n <= 20 est une comparaison chaînée, équivalente à 0 <= n and n <= 20. Si on compte ce and implicite comme une décision, on obtient M = 4. Nous l'avons compté comme une seule condition, comme le font la plupart des outils (aucun opérateur and n'apparaît dans le code).

Récapitulatif

Fonction N E M Classe
C2 moyenne 8 9 3 Simple
C4 nom_jour 10 16 8 Simple
J2 estDisponible 4 5 3 Simple
J6 importer 15 21 8 Simple
P2 signe 6 7 3 Simple
P3 notes_valides 5 6 3 Simple

Partie 2 – Comptage des décisions (M = décisions + 1)

C (exercices.c)

Fonction Décisions M Classe
C1 carre aucune 1 Simple
C2 moyenne 1 for, 1 ternaire ?: 3 Simple
C3 est_bissextile 1 &&, 1 || 3 Simple
C4 nom_jour 7 case 8 Simple
C5 compter_mots 1 while, 4 ||, 1 if, 1 else if 8 Simple
C6 date_valide 4 if/else if, 6 || 11 Modéré

Détail de C3 : il n'y a aucun if, mais && et || sont évalués paresseusement : chacun crée un branchement (si annee % 4 == 0 est faux, annee % 100 != 0 n'est pas évalué). 2 décisions + 1 = 3, ce qui correspond au graphe vu en cours (N = 4, E = 5).

Détail de C4 : le default ne compte pas (c'est le « sinon » du switch) et les return non plus. 7 + 1 = 8, comme avec le graphe (16 − 10 + 2).

Détail de C5 :

Ligne Décision +1
while (texte[i] != '\0') while 1
c == ' ' || c == '\t' || c == '\n' || c == ',' || c == '.' 4 || 5
if (separateur) if 6
else if (!dans_mot) else if 7

M = 7 + 1 = 8. Ici aussi les || comptent sans if : ils sont dans une affectation (int separateur = ...), mais restent des branchements.

Détail de C6 :

Ligne Décision +1
if (annee < 1 || mois < 1 || mois > 12 || jour < 1) if + 3 || 4
if (mois == 2) if 5
if (est_bissextile(annee)) if 6
else if (mois == 4 || mois == 6 || mois == 9 || mois == 11) else if + 3 || 10

M = 10 + 1 = 11, soit juste au-dessus du seuil de 10 (McCabe, PMD) : c'est la fonction C la plus complexe. L'appel à est_bissextile ne compte pas : ses décisions appartiennent à est_bissextile (M calculé par fonction).

Python (exercices.py)

Fonction Décisions M Classe
P1 celsius_vers_fahrenheit aucune 1 Simple
P2 signe 1 if, 1 elif 3 Simple
P3 notes_valides 1 for, 1 if (dans la compréhension) 3 Simple
P4 mention 1 for, 1 if 3 Simple
P5 analyser_mot_de_passe 2 for, 8 if, 2 elif, 2 and, 1 ternaire 16 Modéré

Détail de P5 :

Ligne Décision +1
if len(mdp) < 12 if 1
for c in mdp for 2
if c.isupper() if 3
elif c.islower() elif 4
elif c.isdigit() elif 5
if not (majuscule and minuscule) if + and 7
if not chiffre if 8
if not special if 9
if login and login.lower() in mdp.lower() if + and 11
for i in range(len(mdp) - 2) for 12
if mdp[i] == mdp[i + 1] == mdp[i + 2] if 13
if not problemes if 14
"moyen" if len(problemes) <= 2 else "faible" ternaire 15

M = 15 + 1 = 16. Le else final de la cascade if/elif/else et le break ne comptent pas. Comme pour P3, la comparaison chaînée mdp[i] == mdp[i + 1] == mdp[i + 2] est comptée comme une seule condition.

P4 a le même M que la version refactorisée de mention vue en cours : le dictionnaire remplace la cascade de if/elif (qui aurait donné M = 5).