Semaine 1 : Sémantique et révisions

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.

  1. 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; }
    
  2. 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/Semaine1 et utilisez la commande suivante :

    git add premiers.cpp
    
  3. Définissez dans premiers.cpp une fonction estDiviseur qui prend en argument deux entiers et renvoie true si le premier entier divise le second et false sinon. On rapelle que a % b est le reste de la division de a par b.

    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

  4. 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));
    }
    
  5. Complétez le programme premiers.cpp avec une fonction main qui 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.

  6. La cellule suivante vérifie que premiers.cpp existe, peut être compilé, et que la fonction estDiviseurTest s’exécute sans erreur :

! cppdoctest.py premiers.cpp estDiviseurTest

Exercice 2 (suite)

  1. Un nombre premier est un entier qui a exactement deux diviseurs: \(1\) et lui-même. Définissez dans premiers.cpp une fonction estPremier qui prend en argument un entier et renvoie true si cet entier est un nombre premier et false sinon. Vous devez utiliser la fonction estDiviseur.

    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

  2. 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 fonction main. 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)

  1. 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 fonction estPremier.

    BEGIN SOLUTION

    void premiersBornes(int n) {
        for (int k = 2; k < n; k++) {
    	if (estPremier(k)) {
    	    cout << k << " ";
    	}
        }
    }
    

    END SOLUTION

  2. 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 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é!

    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 transformant premiersBornes pour renvoyer les nombres premiers, par exemple sous forme d’un tableau.

    END SOLUTION

  3. Définissez une fonction premiersPremiers qui 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

  4. Complétez la fonction main pour 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 3: lecture de valeurs bornées

  1. 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.

  2. Définissez une fonction lectureVB (pour lecture Valeur Bornée) qui prend deux paramètres réels, min et 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

  3. 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\)\(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.

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.

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