Semaine 1 : Sémantique et révisions#
TP : révisions: fonctions, programmes, …#
Notes aux enseignants
Le premier exercice est à faire sur papier. Commencez par laisser un peu de temps aux étudiants pour le faire par eux-mêmes puis corriger en mode TD pour s’assurer que la notion de portée des variables est bien acquise.
Les autres exercices se font sur machine, entre autres pour vérifier
que tous les étudiants sont capables de compiler et tester un
programme en C++
et que les fondamentaux sont bien acquis. Leur dire
que ce TP est censé être facile pour eux, ils doivent réussir à tout
faire bien, si ce n’est pas le cas il faut qu’ils posent des questions
et qu’ils fassent des révisions du S1 par eux-mêmes en dehors des
heures de cours.
Ils ne vont pas forcément tout terminer pendant le TP (chacun a son rythme, certains sont très rapides et d’autres très lents), les exercices non faits peuvent être faits après le TP comme entraînement.
À faire
Solutions pour l’exercice banque
Exercice 0 (à la maison)
Téléchargez le devoir «Entrainement» depuis le tableau de bord, et 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 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;
}
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: nombres premiers
Rappel : compilation
Un compilateur est nécessaire pour créer un fichier exécutable à partir d’un fichier source. Vous pouvez utiliser le compilateur clang++ qui est accessible en ligne de commande (terminal).
Pour compiler un programme fichier.cpp
vous pouvez utiliser la
commande suivante:
clang++ -std=c++11 -Wall fichier.cpp -o fichier
L’option -std=c++1
demande au compilateur d’utiliser la norme
ISO C++11
, -Wall
active l’affichage des messages d’avertissement,
fichier.cpp
est le nom du fichier source à compiler et -o fichier
permet de nommer l’exécutable produit.
Créez un fichier
premiers.cpp
et 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 dossierProgAsd/Semaine1
et utilisez la commande suivante :git add premiers.cpp
Définissez dans
premiers.cpp
une fonctionestDiviseur
qui prend en argument deux entiers et renvoietrue
si le premier entier divise le second etfalse
sinon. On rapelle quea % b
est le reste de la division dea
parb
.BEGIN SOLUTION
bool estDiviseur(int a, int b) { return (b % a) == 0; }
Astuce
Les parenthèses ne sont pas indispensables dans ce cas, mais elle peuvent améliorer la lisibilité.
END SOLUTION
Ajoutez dans
premiers.cpp
la 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.cpp
avec une fonctionmain
qui lance les tests deestDiviseur
. 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.cpp
existe, peut être compilé, et que la fonctionestDiviseurTest
s’exécute sans erreur :
! cppdoctest.py premiers.cpp estDiviseurTest
Exercice 2 (suite)
Un nombre premier est un entier qui a exactement deux diviseurs: \(1\) et lui-même. Définissez dans
premiers.cpp
une fonctionestPremier
qui prend en argument un entier et renvoietrue
si cet entier est un nombre premier etfalse
sinon. Vous devez utiliser la fonctionestDiviseur
.Indication
Dans cet exercices 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
bool estPremier(int m) { for (int i = 2; i < m; i++) { if (estDiviseur(m, i)) { 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 \(1\), \(4\), \(6\), \(9\) ne le sont pas. Appelez cette fonction de test depuis la fonctionmain
. Compilez et exécutez le programme, et mettez le au point jusqu’à ce qu’il soit correct.
! cppdoctest.py premiers.cpp estPremierTest
Exercice 2 (suite)
Définissez une fonction
premiersBornes
qui prend en argument un entier \(n\) et qui affiche tous les nombres premiers inférieurs à \(n\). Vous devez utiliser la fonctionestPremier
.BEGIN SOLUTION
void premiersBornes(int n) { for (int k = 2; k < n; k++) { if (estPremier(k)) { cout << k << " "; } } }
END SOLUTION
Complétez la fonction
main
de 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 fonctionpremiersBornes
. 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é!Indication
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
premiersBornes
est 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 transformantpremiersBornes
pour renvoyer les nombres premiers, par exemple sous forme d’un tableau.END SOLUTION
Définissez une fonction
premiersPremiers
qui prend en argument un entier \(n\) et qui affiche les \(n\) premiers nombres premiers. Vous devez utiliser la fonctionestPremier
.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
main
pour qu’elle affiche en plus les \(n\) premiers nombres premiers en faisant appel à la fonctionpremiersPremiers
. 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 3: lecture de valeurs bornées
Créez un nouveau fichier
lectureVB.cpp
et, comme dans l’exercice précédent, signalez le àgit
pour 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,min
etmax
, 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
main
qui fait appel à votre fonction. Compilez, exécutez et vérifiez.
Exercice 4 : 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
Pour les lectures, vous pourrez réutiliser la fonction
lectureVB
quand cela vous paraîtra pertinent.Veillez à découper votre programmes en fonctions de sorte, notamment, à bien séparer calculs et entrées-sorties.
Écrivez des tests.
Exercice 5 : 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.
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
; puis utiliser
le système d’écriture complet qui, toujours par exemple, écrit 1997
comme MCMXCVII
.
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