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.cpp
et 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 commandecd
si nécessaire.L’option
-std=c++11
demande au compilateur d’utiliser la normeISO C++
de 2011,L’option
-Wall
active l’affichage de tous les messages d’avertissement possibles.L’option
-o portee_variables
permet de nommer l’exécutable produit, iciportee_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.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
Dans
premiers.cpp
, définissez une fonctionestDiviseur
spé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
a
etb
, l’expressiona % b
calcule le reste de la division entière dea
parb
.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.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 :
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.cpp
une fonctionestPremier
qui prend en argument un entier naturel et renvoietrue
si cet entier est un nombre premier etfalse
sinon. Vous devez utiliser la fonctionestDiviseur
.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 fonctionmain
. 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
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é!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 4: 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 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
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.
Pour chaque fonction, vous commencerez par écrire la documentation et des tests.
Définir une variable
s
qui 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