# Algo Prog 2021-2022: TP6 (passage par adresse et tests)
###### tags: `algo` `bourg` `c++` `iut` `tp`
## Exercice 1 : passage par adresse
- Ecrivez une fonction ``void compte(char ch[], int& voy, int& cons)`` qui calcule dans les paramètres références ``voy`` et ``cons`` respectivement le nombre de voyelles et de consonnes présentes dans la chaine de caractères ``ch``.
- Dans votre main, testez votre fonction sur plusieurs chaines de caractères.
- Ecrivez maintenant une deuxième fonction ``compte`` qui fait le même calcul, mais **sans utiliser de paramètre référence**. Le passage par adresse devient alors nécessaire.
- Testez cette deuxième fonction.
:::spoiler
````C++
void compte(char ch[], int& voy, int& cons) {
int i = 0;
while (ch[i] != '\0') {
if (ch[i] == 'a' || ch[i] == 'e' || ch[i] == 'i' || ch[i] == 'o' || ch[i] == 'u' || ch[i] == 'y' )
voy++;
else
cons++;
i++;
}
}
void compte2(char ch[], int* voy, int* cons) {
int i = 0;
while (ch[i] != '\0') {
if (ch[i] == 'a' || ch[i] == 'e' || ch[i] == 'i' || ch[i] == 'o' || ch[i] == 'u' || ch[i] == 'y')
(*voy)++;
else
(*cons)++;
i++;
}
}
void main() {
char ch[] = "bonjour"; int v = 0, c = 0;
compte2(ch, &v, &c);
cout << c << v << endl;
}
````
:::
## Exercice 2
Ecrivez une fonction qui prend en paramètre un tableau d'entiers, sa taille, et qui retourne à la fois la valeur du minimum et du maximum de ce tableau. **Dans le code de toute votre fonction, les symboles ``[`` et ``&`` seront interdits**.
Testez votre fonction dans le main sur un tableau initialisé aléatoirement.
:::spoiler
````C++
void minmax(int* tab, int taille, int* min, int* max) {
*min = *tab;
*max = *tab;
for (int i = 0; i < taille; i++) {
if (*(tab + i) > *max)
*max = *(tab + i);
else if (*(tab + i) < *min)
*min = *(tab + i);
}
}
void main() {
int t[100];
for (int i = 0; i < 100; i++)
t[i] = rand() % 1000;
int mini, maxi;
minmax(t, 100, &mini, &maxi);
cout << mini << " " << maxi << endl;
}
````
:::
## Exercice 3 : Parrains de l'IUT
Dans cet exercice, nous allons travailler sur la liste des étudiants de BUT, dans les trois années du diplome. Pour chaque étudiant, les informations qui vont nous intéresser sont:
- le nom
- le prénom
- la promo (BUT 1, 2 ou 3)
- le numéro d'étudiant
Par ailleurs, un étudiant de BUT peut avoir un parrain qui est souvent un étudiant de la promo précédente. En cas de redoublement, il se peut qu'un étudiant retrouve son parrain dans la même promo au cours de son cursus. Par ailleurs, certains étudiants n'ont pas/plus de parrain, c'est souvent le cas par exemple des étudiants de BUT 3.
Dans notre programme, on va modéliser un étudiant par un type structure de la façon suivante:
````C++
struct Etudiant {
int annee; //BUT 1, 2 ou 3
int numero; //numero etudiant unique
Etudiant* parrain = NULL; // pointe vers son parrain-NULL si pas de parrain
char nom[30];
char prenom[30];
};
````
On voit donc que la relation de parrainage est fournie par le pointeur ``parrain`` qui correspond à l'adresse mémoire du parrain dans le cas où celui-ci existe. Par défaut, on a initialisé ce pointeur à ``NULL `` pour indiquer qu'il n'y a pas de relation de parrainage initialement.
1. Dans votre fichier source, déclarez cette structure ainsi qu'une constante entière ``TAILLE`` de valeur 179.
2. Les données concernant l'ensemble des 179 étudiants de BUT sont stockées dans un fichier "liste.csv" disponible ([ici](https://perso.liris.cnrs.fr/eric.duchene/algo/liste.csv)). Ouvrez-le avec le bloc notes pour voir à quoi il ressemble. Il est formaté de la façon suivante: chaque ligne du fichier correspond à un étudiant, et correspond au quadruplet $(numero,prenom,nom,annee)$. Les informations de parrainage ne sont pas présentes dans ce fichier.
Ecrivez une fonction ``void charger_liste_etudiants(const char nom_fichier[], Etudiant tab[])`` qui permet de lire et charger le fichier dont le nom est passé en paramètre dans un tableau d'étudiants de taille TAILLE. Testez-votre fonction dans votre main sur le fichier "liste.csv".
:::spoiler
````C++
void charger_liste_etudiants(char nom_fichier[], Etudiant tab[]) {
char ligne[30];
ifstream entree(nom_fichier, ios::in);
int i = 0;
if (!entree)
cout << "probleme ouverture" << endl;
while (!entree.eof()) {
entree.getline(ligne, 30, ';');
tab[i].numero = atoi(ligne);
entree.getline(tab[i].prenom, 30, ';');
entree.getline(tab[i].nom, 30, ';');
entree.getline(ligne, 30);
tab[i].annee = atoi(ligne);
i++;
}
entree.close();
}
````
:::
3. Ecrivez une fonction ``compte`` qui prend un tableau d'étudiants en paramètre et permet de renvoyer le nombre d'étudiants qui sont en 1ère, en 2ème et en 3ème année. Testez-votre fonction dans le main sur le tableau d'étudiants chargé.
:::spoiler
````C++
void compte(int& but1, int& but2, int& but3, Etudiant tab[]) {
but1 = but2 = but3 = 0;
for (int i = 0; i < TAILLE; i++) {
if (tab[i].annee == 1)
but1++;
else if (tab[i].annee == 2)
but2++;
else if (tab[i].annee == 3)
but3++;
}
}
````
:::
4. Ecrivez une fonction ``Etudiant* trouver(int numero, Etudiant tab[]) `` qui, à partir du numéro d'étudiant passé en paramètre, retourne l'adresse mémoire de l'étudiant dans le tableau ``tab`` ayant ce numéro. Dans le cas où le numéro ne correspond à aucun étudiant du tableau, vous retournerez NULL.
:::spoiler
````C++
Etudiant* trouver(int n, Etudiant tab[]) {
for (int i = 0; i < TAILLE; i++) {
if (tab[i].numero == n)
return &tab[i];
}
return NULL;
}
````
:::
5. Les informations de parrainages sont incluses dans le fichier "parrains.csv" que vous trouverez ([ici](https://perso.liris.cnrs.fr/eric.duchene/algo/parrains.csv)). Il est formaté de la façon suivante:
``
(numero_etudiant, numero_de_son_parrain)
``
et chaque ligne correspond à un étudiant. Pour les étudiants qui n'ont pas de parrain, il n'y a pas de deuxième numéro sur la ligne.
Ecrivez une fonction qui permette de lire ce fichier et de mettre à jour les pointeurs ``parrain`` de chaque étudiant du tableau pour les étudiants qui possèdent un parrain.
:::spoiler
````C++
void charger_liste_parrains(char nom_fichier[], Etudiant tab[]) {
char ligne[30], ligne2[30];
ifstream entree(nom_fichier, ios::in);
int i = 0;
int num, numparrain;
if (!entree)
cout << "probleme ouverture" << endl;
while (!entree.eof()) {
entree.getline(ligne, 30, ';');
num = atoi(ligne);
entree.getline(ligne2, 30);
Etudiant* etu = trouver(num, tab);
Etudiant* parrain = NULL;
if (strlen(ligne2) > 2) { //cas où il y a un deuxième numero et donc un parrain
numparrain = atoi(ligne2);
parrain = trouver(numparrain, tab);//on cherche l'adresse du parrain
}
if (etu != NULL && parrain == NULL) // pas de parrain
etu->parrain = NULL;
else if (etu != NULL && parrain != NULL)
etu->parrain = parrain;
}
entree.close();
}
````
:::
6. Ecrivez une fonction ``void affiche_etu(Etudiant& etu)`` qui affiche les informations sur un étudiant passé en paramètre de la façon suivante:
````
Prenom: JEAN Nom: LOUVAIN Annee: 2 Numero: 328637 Parrain: oui
````
Pour l'information de parrainage, pour le moment vous indiquerez seulement si l'étudiant a un parrain ou non.
:::spoiler
````C++
void affiche_etu(Etudiant& etu) {
cout << "Prenom:" << etu.prenom << " Nom:" << etu.nom << " Annee:" << etu.annee << " Numero:" << etu.numero;
if (etu.parrain != NULL)
cout << " Parrain: oui" << endl;
else
cout << " Parrain: non" << endl;
}
````
:::
7. Ecrivez une fonction ``void affiche_parrain_1A(Etudiant tab[])`` qui affiche les informations de parrainage de tous les étudiants de BUT 1 sous la forme suivante:
````
JEAN DUPONT a pour parrain/marraine NOEMIE LILAS
````
Testez votre fonction.
:::spoiler
````C++
void affiche_parrain_1A(Etudiant tab[]) {
for (int i = 0; i < TAILLE; i++) {
if (tab[i].annee == 1 && tab[i].parrain != NULL) {
cout << tab[i].prenom << " " << tab[i].nom << " a pour parrain/marraine " << tab[i].parrain->prenom << " " << tab[i].parrain->nom << endl;
}
}
}
````
:::
8. Ecrivez une fonction ``void changerParrain(Etudiant& etu1, Etudiant& etu2) `` qui échange les parrains des deux étudiants passés en paramètre.
:::spoiler
````C++
void changerParrain(Etudiant& etu1, Etudiant& etu2) {
if (etu1.parrain != NULL && etu2.parrain != NULL) {
Etudiant* tmp = etu1.parrain;
etu1.parrain = etu2.parrain;
etu2.parrain = tmp;
}
}
````
:::
9. Ecrivez une fonction ``void affiche_filleuls_2A(Etudiant tab[])`` qui, pour tous les étudiants de BUT 2, affiche la liste de ses filleuls (il peut en avoir plusieurs) de la façon suivante:
````
JEAN DUPONT a pour filleul.e.s ALAIN ROBERT, PAULE LALOU
CARINE MARCHE n'a pas de filleul.e.
````
Testez votre fonction. Y-a-t-il des étudiant.e.s sans filleul ? Lesquel.le.s ?
:::spoiler
````C++
//un etudiant de 2A peut avoir plusieurs filleuls
void affiche_filleuls_2A(Etudiant tab[]) {
for (int i = 0; i < TAILLE; i++) {
if (tab[i].annee == 2) {
int nb_filleuls = 0;
for (int j = 0; j < TAILLE; j++) {
if (&tab[i] == tab[j].parrain) {//i est le parrain de j
nb_filleuls++;
if (nb_filleuls == 1) {
cout << "L'etudiant.e " << tab[i].prenom << " " << tab[i].nom << " a pour filleul.e(s) " << tab[j].prenom << " " << tab[j].nom;
}
else
cout << ", " << tab[j].prenom << " " << tab[j].nom;
}
}
if (nb_filleuls == 0) {
cout << "L'etudiant.e " << tab[i].prenom << " " << tab[i].nom << " n'a pas de filleul.e";
}
cout << endl;
}
}
}
````
:::
10. Y a-t-il des étudiant.e.s d'une même promotion qui sont parrains entre eux ? Ecrivez une fonction qui permette de répondre à cette question et affichez tous les couples de la sorte.
:::spoiler
````C++
int affiche_etudiants_meme_promo_parrains_entre_eux(Etudiant tab[]) {
int cpt = 0;
for (int i = 0; i < TAILLE; i++) {
//meme promo
if (tab[i].parrain != NULL && tab[i].annee == tab[i].parrain->annee) {
cout << "couple filleul-parrain meme promo" << endl;
affiche_etu(tab[i]);
affiche_etu(*(tab[i].parrain));
cpt++;
}
}
return cpt;
}
````
:::
11. Ecrivez une fonction qui retourne le nombre d'étudiants qui sont à la fois parrain et filleul.e. Combien y en a-t-il ?
## Exercice 4
En utilisant le fichier dictionnaire du TP2 ([ici](https://perso.liris.cnrs.fr/eric.duchene/algo/DicFra.csv)), quels sont les mots les plus longs du dictionnaire qui n'ont que des lettres différentes ? (pas de récursivité dans cette question).
<!--
## Exercice 4: tests
Une partie importante dans la construction d'un programme est la confection de jeux de tests efficaces. Par exemple, si l'on vous demande d'écrire une fonction de calcul du pgcd de deux nombres et que vous ne testez cette fonction que sur 1 et 3, cela ne garantit pas pour autant que votre fonction réalise bien le calcul voulu en général.
Nous verrons au S2 (en Java) plusieurs façons d'automatiser ce type de test, via des tests que l'on appelle *unitaires*. En guise d'approche, nous allons vous sensibiliser à ce type de test avec l'exercice suivant.
Etant donnés trois entiers ``a,b,c``, on cherche à écrire une fonction qui retourne la valeur médiane, c'est-à-dire celle qui est comprise entre les deux autres. Par exemple:
- si a=2, b=5 et c=1, alors la médiane est 2.
- si a=3, b=2, et c=3, alors la médiane est 3.
Voici ci-dessous trois fonctions
````code=C++
int f1(int a, int b, int c){
int median;
if (a<= c && c<=b || b<=c && c<=a)
median = c;
else if (b<=a && a<=c || c<=a && a<=b)
median = a;
else
median = b;
return median;
}
````
````
# Solution 2
if a>=b and a<=c:
median=a
else if (b>=a and b<=c)
median=c
else if (c>=a and c<=b:
median=c
return median
````
````
# Solution 3
median = a
if a < b < c and c < b < a :
median = b
if b < c < a and a < c < b :
median = c
return median
````
-->