Quelques remarques en attendant les notes definitives. Les copies se separent en deux moities assez distinctes, la premiere de niveau encore tres faible. Un quart des copies sont de tres bon niveau. Sans surprise, la partie I a ete globalement bien traitee. Le traitement de la partie III montre en revanche qu'a trop peu d'exceptions pres, la representation des listes chainees et la notion de recurrence ont ete presque totalement incomprises. J'ai du adapter la bareme a cette situation, mais il reste malgre tout a la majorite d'entre vous a comprendre ces deux notions, reellement importantes pour votre formation. Sans surprise non plus (au moins en ce qui me concerne) la partie II a ete traitee avec plus ou moins de bonheur, parfois de facon concise et efficace, parfois avec une confusion extreme (explosion du nombre de variables et de structures de controle, idee indechiffrable d'un affichage par tranches de largeur k, boucles paralleles, syntaxe semi-stochastique de semantique vague, etc.) : c'est essentiellement sur elle que se determine le niveau global d'une copie. -------- Partie I -------- Un exercice elementaire. On parcourt chaque ligne de m. Les valeurs avant l'element sur la diagonale doivent etre toutes non nulles, celles strictement apres cet element doivent etre toutes nulles. int tr_str(int m[N][N]) { int i, j; for (i = 0; i < N; i++) { for (j = 0; j <= i; j++) if (!m[i][j]) return 0; for (j = i + 1; j < N; j++) if (m[i][j]) return 0; } return 1; } --------- Partie II --------- Trois exercices classiques, de difficulte moyenne, et ne se servant que des elements les plus simples du langage (for, if, while). Les traitements demandes n'etaient pas en eux-memes tres compliques, mais leur implementation demandait un peu de soin et de methode. ---------- Question 1 ---------- Dans le code ci-dessous, la variable c a pour valeur, en fin d'execution du bloc-for, le numero de la prochaine colonne d'affichage. lorsque le caractere courant de s n'est pas '\n' et si c depasse k (2), il faut afficher un retour a la ligne supplementaire avant l'affichage de ce caractere (en 4). void afficher_page(int k, char *s) { int c = 0, i = 0; for (i = 0; s[i] != '\0'; i++) { if (s[i] == '\n') { // (1) c = 0; } else if (c > k) { // (2) c = 1; printf("\n"); } else c++; // (3) printf("%c", s[i]); // (4) } } ---------- Question 2 ---------- Pour determiner la taille d'allocation, il suffit de reprendre presque litteralement le code precedent, en remplacant l'instruction d'affichage d'un retour supplementaire (dans 2) par l'incrementation d'un compteur (n++). La valeur finale de ce compteur est le nombre total de retours a la ligne ajoutes par la fonction precedente. Pour construire la chaine, on reprend une nouvelle fois le meme code, en remplacant chaque affichages par une ecriture dans la chaine-resultat - celle-ci disposant de son propre compteur d'ecriture (j). char *mettre_en_page(int k, char *s) { int n = 0, c = 0, i, j; char *r; for (i = 0; s[i] != '\0'; i++) { if (s[i] == '\n') { // (1) c = 0; } else if (c > k) { // (2) c = 1; n++; } else c++; // (3) } r = malloc(i + n + 1); j = 0; for (i = 0; s[i] != '\0'; i++) { if (s[i] == '\n') { // (1) c = 0; } else if (c > k) { // (2) c = 1; r[j++] = '\n'; } else c++; // (3) r[j++] = s[i]; // (4) } r[j] = '\0'; return r; } ---------- Question 3 ---------- Pour chaque ligne : - on cherche la fin de cette ligne (1) - si la ligne fait moins de k caracteres (2), on ajoute a un compteur (n) le nombre d'espaces que l'on doit lui ajouter (3). La valeur finale du compteur indique le nombre total d'espaces a ajouter. Il faut ensuite reconstruire la chaine resultat. Pour chaque ligne : - on recopie dans la chaine-resultat tous les caracteres avant son retour a la ligne (4) - si la ligne fait moins de k caracteres, on la complete dans la chaine-resultat par des espaces (5) - on termine dans la chaine-resultat la ligne courante par un retour-chariot (6). char *aligner_retours(int k, char *s) { int i = 0, j, p = 0, n = 0; char *r; while (s[i] != '\0') { p = 0; while(s[i] != '\n') { // (1) i++; p++; } if (k > p) // (2) n += k - p; // (3) i++; } r = malloc((i + n + 1)*sizeof(char)); i = 0; j = 0; while (s[i] != '\0') { p = 0; while(s[i] != '\n') { // (4) r[j++] = s[i]; i++; p++; } while (p < k) { // (5) r[j++] = ' '; p++; } r[j++] = '\n'; // (6) i++; } r[j] = '\0'; return r; } ---------- Partie III ---------- Trois exercices plutot academiques, demandant un minimum de reflexion et necessitant d'avoir bien compris la notion de recurrence. A noter que dans plusieurs copies, l'enonce a manifestement ete mal (ou pas ?) lu, la reponse a la premiere question n'etant pas celle demandee (extraction des clefs impaires, au lieu des clefs de positions impaires). Dans les quelques copies ayant traite cette partie, le traitement est souvent confus : melange d'iteration et de recurrence, confusions entre pointeurs, appels recursifs imperatifs, affectation de champs de cellules deja liberees, etc. ---------- Question 1 ---------- Il faut, a chaque appel, suivre le chainage jusqu'a la cellule de position 2 (lorsqu'elle existe), et faire l'appel recursif sur la sous-liste commencant a cette position. Il faut aussi liberer la cellule de position 0. struct cell *clefs_impaires(struct cell *pc) { struct cell *pcs; if (pc == NULL) return NULL; pcs = pc -> suiv; free(pc); if (pcs == NULL) return NULL; pcs -> suiv = clefs_impaires(pcs -> suiv); return pcs; } ---------- Question 2 ---------- Si k > 0, on libere la cellule de position 0, on relance sur la liste commencant a la position 1 en faisant decroitre k. Sinon, la cellule de position 0 fait partie de la liste resultat, et on relance sur la liste commencant a la position 1, avec k == n-1. struct cell *sous_suite(int k, int n, struct cell *pc) { struct cell *pcs; if (pc == NULL) return NULL; if (k > 0) { pcs = pc -> suiv; free(pc); return sous_suite(k - 1, n, pcs); } pc -> suiv = sous_suite(n - 1, n, pc -> suiv); return pc; } ---------- Question 3 ---------- On echange les cellules de positions 0 et 1 (1), puis on relance sur la liste commencant a la position 1 apres l'echange (en 2, avec relance et fin de l'echange effectues en une seule instruction) struct cell *rotation_gauche(struct cell *pc) { stuct cell *pcs; if (pc == NULL) return NULL; if (pc -> suiv == NULL) return pc; pcs = pc -> suiv; // (1) pc -> suiv = pcs -> suiv; pcs -> suiv = rotation_gauche(pc); // (2) return pcs; } ---------- Question 4 ---------- Le traitement est symetrique : on relance sur la liste commencant a la position 1 (1), puis on echange les cellules de position 0 et 1 dans la liste obtenue apres l'appel (2). struct cell *rotation_droite(struct cell *pc) { stuct cell *pcs; if (pc == NULL) return NULL; if (pc -> suiv == NULL) return pc; pc -> suiv = rotation_droite(pc -> suiv); // (1) pcs = pc -> suiv; // (2) pc -> suiv = pcs -> suiv; pcs -> suiv = pc; return pcs; }