Files
2026_TD2_DEV51_Qualite_Algo/COMPTE_RENDU.md
T

140 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](Graphe/C2.jpg)
- N = 8, E = 9, P = 1
- **M = 9 − 8 + 2 × 1 = 3**
### C4 – `nom_jour` (C)
![Graphe C4](Graphe/C4.jpg)
- 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**
### J2 – `estDisponible` (Java)
![Graphe J2](Graphe/J2.png)
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 = 4, E = 5, P = 1
- **M = 5 − 4 + 2 × 1 = 3**
### J6 – `importer` (Java)
![Graphe J6](Graphe/J6.png)
Les nœuds de décision sont en jaune, et la condition de la boucle `for` est en bleu.
- N = 15, E = 21, P = 1
- **M = 21 − 15 + 2 × 1 = 8**
### P2 – `signe` (Python)
![Graphe P2](Graphe/P2.jpg)
- N = 6, E = 7, P = 1
- **M = 7 − 6 + 2 × 1 = 3**
### P3 – `notes_valides` (Python)
![Graphe P3](Graphe/P3.jpg)
La compréhension de liste cache une boucle et un filtre : elle se lit comme un `for` contenant un `if`.
- N = 5, E = 6, P = 1
- **M = 6 − 5 + 2 × 1 = 3**
### 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).