Semaine 1 : Sémantique et révisions#
TP : révisions: fonctions, programmes, …#
À faire
Solutions pour l’exercice banque
Exercice 0
- Téléchargez le devoir «Entrainement» depuis le tableau de bord. 
- À la maison, effectuez une dizaine d’exercices pour réviser, ou plus selon vos besoins. 
Exercice 1: portée des variables (sur papier)
On considère le programme ci-dessous qui, incidemment, n’est pas un exemple de bonne programmation. Précisez la portée de chacune des variables et donnez les valeurs affichées.
int x = 10;
int main() {
    {
        int x = 5;
        cout << "1) x vaut :" << x << endl;
    }
    {
        cout << "2) x vaut :" << x << endl;
    }
    for (int x = 0; x < 3; x++) {
        cout << "3) x vaut :" << x << endl;
    }
    cout << "4) x vaut :" << x;
    {
        cout << "5) x vaut :" << x << endl;
        int x = 7;
    }
    for (x = 0; x < 3; x++) {
        cout << "6) x vaut :" << x << endl;
    }
    cout << "7) x vaut :" << x << endl;
    return 0;
}
Indication
Si vous avez un doute sur votre solution, vous pouvez copier le code
ci-dessus dans un fichier que vous compilerez et exécuterez. Voir les
rappels sur la compilation ci-dessous. Comme le programme utilise
cout, il faudra ajouter l’entête suivant:
#include <iostream>
using namespace std;
BEGIN SOLUTION
- En 1, on accède à la variable \(x\) du bloc courant, on affiche donc 5. 
- En 2, on est maintenant dans un deuxième bloc, la variable du bloc précédent n’est donc plus visible et l’on accède donc à la variable globale et affiche 10. 
- Ensuite trois passages dans la boucle vont être réalisés et afficher en 3 les valeurs 0, 1 et 2 en réalisant un accès à la variable locale de la boucle. 
- En 4, comme en 2, plus aucune variable locale n’est accessible et l’on accède donc à la variable globale afin d’afficher 10. 
- En 5, une variable locale \(x\) est déclarée dans le bloc mais après l’affichage, une nouvelle fois c’est donc la valeur 10 de la variable globale qui est affichée et la variable locale n’est pas utilisée. 
- En 6, l’affichage sera exactement le même que pour la première boucle mais tout se fait en utilisant la variable globale à la place de la variable locale. 
- En 7 on affiche une dernière fois la variable globale mais celle-ci vaut maintenant 3 car elle a été modifiée par la boucle. 
END SOLUTION
Exercice 2: compilation, exécution, espace de travail
Vous allez maintenant exécuter le programme de l’exercice précédent afin de vérifier votre solution. Pour cela, il faudra préalablement le compiler, c’est-à-dire transformer le programme en binaire directement exécutable par l’ordinateur.
- Créez un fichier - portee_variables.cppet copiez y le programme de l’exercice précédent.
- Organisez votre espace de travail et à ajustez la taille des caractères (avec Ctrl-+ et Ctrl--) pour avoir une vue confortable simultanée de tous les éléments requis : consignes, code, terminal.   - Fig. 1 Vidéo: organiser son espace de travail avec JupyterLab# 
- Essayez de compiler le programme avec la commande suivante dans le terminal : - clang++ -std=c++11 -Wall portee_variables.cpp -o portee_variables - Indications - Veillez à créer le programme dans le dossier - Semaine1.
- Veillez à lancer la compilation depuis le même dossier - Semaine1. Utilisez la commande- cdsi nécessaire.
- L’option - -std=c++11demande au compilateur d’utiliser la norme- ISO C++de 2011,
- L’option - -Wallactive l’affichage de tous les messages d’avertissement possibles.
- L’option - -o portee_variablespermet de nommer l’exécutable produit, ici- portee_variables
 - La compilation doit échouer avec une erreur - use of undeclared identifier 'cout'. À quelle ligne? Quelle colonne? Que signifie ce message d’erreur?
- Ajoutez au début du programme les deux lignes suivantes (à quoi servent-elles?) et recompilez le : - #include <iostream> using namespace std; - Cette fois, il ne doit pas y avoir d’erreur. 
- Exécutez le programme compilé obtenu avec la commande suivante : - ./portee_variables 
Exercice 3 : nombres premiers
- Créez un fichier - premiers.cppet recopiez au début de celui-ci les trois lignes suivantes :- #include <iostream> using namespace std; #define CHECK(C) if ( !(C) ) { std::cerr << "Test failed: "#C << std::endl; } 
- Pour que ce fichier soit transmis lorsque vous déposerez votre devoir, il faut le signaler au gestionnaire de version - git. Pour cela, ouvrez le terminal, déplacez vous si nécessaire dans le dossier- ProgAsd/Semaine1et utilisez la commande suivante :- git add premiers.cpp 
- Dans - premiers.cpp, définissez une fonction- estDiviseurspécifiée par la documentation suivante:- /** test de division entière * @param p un entier non nul * @param q un entier * @return true si p divise q et false sinon */ - Indication - On rappelle que, pour deux entiers - aet- b, l’expression- a % bcalcule le reste de la division entière de- apar- b.- BEGIN SOLUTION - bool estDiviseur(int p, int q) { return (q % p) == 0; } - Astuce - Les parenthèses ne sont pas indispensables dans ce cas, mais elle peuvent améliorer la lisibilité. - END SOLUTION 
- Ajoutez dans - premiers.cppla fonction de tests suivante :- void estDiviseurTest() { CHECK( estDiviseur(1, 15) ); CHECK( not estDiviseur(2, 15) ) CHECK( estDiviseur(3, 15) ); CHECK( estDiviseur(5, 15) ); CHECK( estDiviseur(15, 15)); } 
- Complétez le programme - premiers.cppavec une fonction- mainqui lance les tests de- estDiviseur. Depuis votre terminal, compilez et exécutez le programme, et mettez le au point jusqu’à ce qu’il soit correct.
- La cellule suivante vérifie que - premiers.cppexiste, peut être compilé, et que la fonction- estDiviseurTests’exécute sans erreur :
Exercice 3 (suite)
- Un nombre premier est un entier naturel qui a exactement deux diviseurs: \(1\) et lui-même. Spécifiez et définissez dans - premiers.cppune fonction- estPremierqui prend en argument un entier naturel et renvoie- truesi cet entier est un nombre premier et- falsesinon. Vous devez utiliser la fonction- estDiviseur.- Indication - Dans cet exercice et les suivants, on ne cherchera pas à optimiser les algorithmes. L’objectif est de travailler les bases de la programmation et notamment les fonctions. - BEGIN SOLUTION - /** test de primalité * @param m un entier naturel * @return true si m est un nombre premier et false sinon */ bool estPremier(int m) { if ( m <= 1 ) return false; for ( int d = 2; d < m; d++ ) if ( estDiviseur(d, m) ) return false; return true; } - END SOLUTION 
- Comme précédemment, définissez une fonction de test pour - estPremier, vérifiant par exemple que \(2\), \(3\), \(5\), \(7\) sont premiers mais que \(0\), \(1\), \(4\), \(6\), \(9\) ne le sont pas. Appelez cette fonction de test depuis la fonction- main. Compilez et exécutez le programme, et mettez le au point jusqu’à ce qu’il soit correct.- BEGIN SOLUTION - void estPremierTest() { CHECK( estPremier(2) ); CHECK( estPremier(3) ); CHECK( estPremier(5) ); CHECK( estPremier(7) ); CHECK( not estPremier(0) ); CHECK( not estPremier(1) ); CHECK( not estPremier(4) ); CHECK( not estPremier(6) ); CHECK( not estPremier(9) ); } - END SOLUTION 
Exercice 3 (suite)
- Définissez une fonction - premiersBornesqui prend en argument un entier \(n\) et qui affiche tous les nombres premiers inférieurs à \(n\). Vous devez utiliser la fonction- estPremier.- BEGIN SOLUTION - void premiersBornes(int n) { for (int k = 2; k < n; k++) { if (estPremier(k)) { cout << k << " "; } } } - END SOLUTION 
- Complétez la fonction - mainde votre programme pour qu’elle demande à l’utilisateur un entier \(n\) puis qu’elle affiche les nombres premiers inférieurs à \(n\) en faisant appel à la fonction- premiersBornes. Compilez, exécutez et vérifiez votre programme. Par exemple, si l’utilisateur entre 12, les nombres affichés doivent être : 2, 3, 5, 7 et 11. Attention, 9 ne doit pas être affiché!- Note - Pourquoi n’a t’on pas écrit de fonction de test pour - premiersBornes? Comment pourrait-on s’y prendre pour autrement pour pouvoir automatiser les tests?- BEGIN SOLUTION - premiersBornesest une procédure qui affiche son résultat plutôt que de le renvoyer. Pour pouvoir tester la logique interne, il faudrait découpler le calcul et l’affichage, en transformant- premiersBornespour renvoyer les nombres premiers, par exemple sous forme d’un tableau.- END SOLUTION 
- Définissez une fonction - premiersPremiersqui prend en argument un entier \(n\) et qui affiche les \(n\) premiers nombres premiers. Vous devez utiliser la fonction- estPremier.- BEGIN SOLUTION - void premiersPremiers(int n) { int compteur = 0; int nombre = 2; while (compteur < n) { if (estPremier(nombre)) { cout << nombre << " "; compteur = compteur + 1; } nombre = nombre + 1; } } - END SOLUTION 
- Complétez la fonction - mainpour qu’elle affiche en plus les \(n\) premiers nombres premiers en faisant appel à la fonction- premiersPremiers. Compilez, exécutez et vérifiez votre programme. Par exemple, si l’utilisateur saisit 5, les nombres affichés sont 2, 3, 5, 7 et 11. Si l’utilisateur entre 12, alors 12 nombres sont affichés.
Exercice 4: lecture de valeurs bornées
- Créez un nouveau fichier - lectureVB.cppet, comme dans l’exercice précédent, signalez le à- gitpour qu’il soit transmis lorsque vous déposerez votre devoir. Vous devrez par la suite répéter cette procédure pour chaque exercice.
- Définissez une fonction - lectureVB(pour lecture Valeur Bornée) qui prend deux paramètres réels,- minet- max, puis demande à l’utilisateur de saisir une valeur réelle comprise entre ces deux bornes, et renvoie cette valeur. Par exemple, on peut l’utiliser pour demander de saisir un nombre d’heures en imposant qu’il soit compris entre \(0\) et \(23\).- La fonction doit afficher un message d’erreur si la valeur saisie n’est pas comprise entre les bornes et re-demander à l’utilisateur une nouvelle saisie (en précisant quelle est l’erreur) jusqu’à pouvoir renvoyer une valeur correcte. - BEGIN SOLUTION - float lectureVB(float min, float max) { // On verifie que les bornes sont correctes sinon on inverse if (min > max) { float temp = min; min = max; max = temp; } // La saisie avec une boucle do while float res; do { cout << "Saisissez un entier compris entre " << min << " et " << max ; cin >> res ; if (res < min) { cout << "Recommencez, valeur trop petite" << endl; } else if (res > max) { cout << "Recommencez, valeur trop grande" << endl; } } while ((res < min) or (res > max)); return res; } - END SOLUTION 
- Complétez le programme avec une fonction - mainqui fait appel à votre fonction. Compilez, exécutez et vérifiez.
Exercice 5 : banque
Une banque fait un prêt à une personne pour un montant total de \(s_0\) euros. Cette personne rembourse chaque mois un montant fixe \(r\) et paye en plus un intérêt variable \(i = i_m \times s\) où \(i_m\) est le taux d’intérêt mensuel fixe et \(s\) la somme restant à rembourser (avant déduction du remboursement mensuel).
Écrivez un programme banque.cpp qui demande à l’utilisateur le
montant emprunté (\(s_0\)), le montant remboursé par mois (\(r\)) et
le taux d’intérêt mensuel (\(i_m\), qui doit être compris entre
\(0.1\%\) et \(1.5\%\) pour ne pas dépasser le taux d’usure annuel
fixé à \(19.96\%\)), puis qui calcule et affiche la durée du
remboursement et la somme des intérêts versés.
Indications et bonnes pratiques
- Pour les lectures, vous pourrez réutiliser la fonction - lectureVBquand cela vous paraîtra pertinent.
- Veillez à découper votre programmes en fonctions de sorte, notamment, à bien séparer calculs et entrées-sorties. 
- Pour chaque fonction, vous commencerez par écrire la documentation et des tests. 
- Définir une variable - squi représente la somme due à la banque au début du mois (on appelle cette propriété un invariant), et faire évoluer au fil des mois cette variable jusqu’à ce qu’il n’y ait plus rien à rembourser.
BEGIN SOLUTION
À faire
END SOLUTION
Exercice 6 : chiffres romains
Dans un fichier romains.cpp, définissez une fonction
chiffresRomains qui prend un entier strictement positif en paramètre
et renvoie une chaîne de caractères contenant sa représentation en
chiffres
romains. Par
exemple, chiffresRomains(1997) renverra MCMXCVII.
Indication
Pour vous entraîner vous pourrez commencer par définir une fonction
chiffresRomainsBasique utilisant un système d’écriture simplifié
qui, par exemple, écrit 1997 comme  MDCCCCLXXXXVII.
Complétez votre programme avec des tests; compilez, exécutez, mettez au point.
BEGIN SOLUTION
La première version est relativement simple :
string chiffresRomainsBasique(int n) {
    string résultat = "";
    while (n >= 1000) { résultat += "M";  n = n - 1000; }
    if    (n >=  500) { résultat += "D";  n = n -  500; }
    while (n >=  100) { résultat += "C";  n = n -  100; }
    if    (n >=   50) { résultat += "L";  n = n -   50; }
    while (n >=   10) { résultat += "X";  n = n -   10; }
    if    (n >=    5) { résultat += "V";  n = n -    5; }
    while (n >=    1) { résultat += "I";  n = n -    1; }
    return résultat;
}
La deuxième n’est pas beaucoup plus complexe mais demande de l’attention pour bien envisager tous les cas possibles :
string chiffresRomainsBasique(int n) {
    string résultat = "";
    while (n >= 1000) { résultat += "M";  n = n - 1000; }
    if    (n >=  500) { résultat += "D";  n = n -  500; }
    while (n >=  100) { résultat += "C";  n = n -  100; }
    if    (n >=   50) { résultat += "L";  n = n -   50; }
    while (n >=   10) { résultat += "X";  n = n -   10; }
    if    (n >=    5) { résultat += "V";  n = n -    5; }
    while (n >=    1) { résultat += "I";  n = n -    1; }
    return résultat;
}
END SOLUTION