Choisissez votre style : style 1,    style 2,    sans commentaire,    impression,    ancien.

Correction
Les fonctions (2)

Exercice 1 Exercice 2 Exercice 3 Exercice 4 Exercice 5 Exercice 6 Exercice 7

Exercice 1 : Surcharge de fonction (niveau 1)

Exercice n°13 (pages 31 et 212) de l'ouvrage C++ par la pratique.

En C++, une fonction est identifiée par son nom et par les types de ses paramètres. On peut en effet imaginer que certaines fonctions se comportent différemment selon le type de paramètre transmis : on n'inverse par exemple pas une matrice de la même manière que l'on inverse un nombre réel.

Dans cet exercice par contre, le contenu de la fonction n'est pas modifié car on échange toujours 2 objets de la même façon. Ce qui change par contre ici, c'est le type des paramètres et de la variable intermédiaire.

void echange(double& a, double& b)
{
  double copie(a);
  a=b;
  b=copie;
}

void echange(char& a, char& b)
{
  char copie(a);
  a=b;
  b=copie;
}
[avancé !] Notez que le langage C++ offre une possibilité de faire ce genre de surcharges plus facilement : définir des fonctions indépendamment du type de leurs arguments. Ce sont les templates, que nous verrons au second semestre.

C'est un peu comme si on écrivait la fonction générique :

void echange(<type>& a, <type>& b)
{
  <type> copie(a);
  a = b;
  b = copie;
}

justement sans avoir à préciser le type.

La façon correct de l'écrire en C++ est :

template<typename Type>
void echange(Type& a, Type& b)
{
  Type copie(a);
  a = b;
  b = copie;
}
[avancé !] [C+++11] Notez par ailleurs que depuis la version 2011, le langage C++ offre une possibilité de faire cela de façon plus efficace, en évitant les copies lorsque cela est approprié, en utilisant la «sémantique de déplacement» («move semantics»). Ceci est une optimisation avancée ; à n'utiliser que si vous savez exactement ce que vous faites :
template<typename Type>
void echange(Type& a, Type& b)
{
  b = std::move(tmp);     // Cela rend tmp invalide (mais peu importe ici puisqu'on ne l'utilise plus)
}

Exercice 2 : factorielle

Exercice n°32 (pages 81 et 251) de l'ouvrage C++ par la pratique.
  • Pour la fonction factorielleIterative(), il s'agit d'une simple boucle for. Il faut simplement veiller aux bonnes conditions initiales :
    
                        Je vous conseille de toujours commenter vos programmes, et
                        en particulier de dire ce que fait chaque fonction, quels sont ses
                        arguments et à quoi correspond la valeur de retour.
        
  • Pour la fonction factorielleRecursive() :
    
    

    Cette fonction récursive est construite sur le schéma vu en cours :

    1. Détermination de la condition d'arrêt :
      if (n == 0) return 1;
      On pourrait même optimiser un peu plus et écrire  :
      if (n <= 1) return 1;
    2. Écriture de la relation de récurrence :
      n * factorielleRecursive(n-1)
  • Il faut maintenant écrire le corps principal du programme (fonction main()).

    La base est assez simple :

    1. Déclarer une variable entière :
      unsigned int n;
    2. Demander sa valeur. Tant qu'à faire pourquoi ne pas le faire lors de l'initialisation de cette variable ? :
      unsigned int n(demander_nombre(0, 12));
    3. Appeler successivement les deux méthodes et afficher les résultats :
                          cout << "Méthode itérative :" << endl;
                          cout << "    " << n << "! = " << factorielleIterative(n) << endl;
                          cout << "Méthode récursive :" << endl;
                          cout << "    " << n << "! = " << factorielleRecursive(n) << endl;
                          
  • La boucle demandant à l'utilisateur s'il souhaite recommencer se fait de façon usuelle.
  • On a donc finalement le programme suivant :

    #include <iostream>
    #include <limits>
    #include <string>
    using namespace std;
    
    /* ----------------------------------------------------------------------
     * Sortie : n!
     * ---------------------------------------------------------------------- */
    unsigned int factorielleIterative(unsigned int n)
    {
      unsigned int fact(1);
      for (unsigned int i(2); i <= n; ++i)
        fact *= i;
      return fact;
    }
    
    /* ----------------------------------------------------------------------
     * Sortie : n!
     * ---------------------------------------------------------------------- */
    unsigned int factorielleRecursive(unsigned int n)
    {
      if (n == 0)
        return 1;
      else
        return (n * factorielleRecursive(n-1));
    }
    
    /* --------------------------------------------------------------
     * si b < a.
     * Sortie : le nombre saisi par l'utilisateur
     * -------------------------------------------------------------- */
    unsigned int demander_nombre(unsigned int a, unsigned int b)
    {
        unsigned int res;
    
        do {
          cout << "Entrez un nombre entier ";
          if (a >= b)
          else
    	cout << "compris entre " << a << " et " << b;
          cout << " : ";
          cin >> res;
          if (cin.fail()) { 
    	     << "pas du charabia !" << endl;
            cin.clear();
    	// "jette" toute la ligne
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
    	res = a-1;
          }
        } while ((res < a) or ((a < b) and (res > b)));
    
       return res;
    }
    
    // ----------------------------------------------------------------------
    int main()
    {
      char rep;
    
      do {
        unsigned int n(demander_nombre(0, 12));
        cout << "    " << n << "! = " << factorielleIterative(n)
             << endl;
        cout << "    " << n << "! = " << factorielleRecursive(n)
             << endl;
    
        do {
          cout << "Voulez-vous recommencer [o/n] ? ";
          cin >> rep;
        } while ((rep != 'o') and (rep != 'n'));
      } while (rep == 'o');
    
      return 0;
    }
    
    

    Exercice 3 : nombres de Fibonacci

    Exercice n°33 (pages 82 et 254) de l'ouvrage C++ par la pratique.

    Cet exercice est très similaire au précédent et se fait de la même façon.

    Le programme complet est :

    #include <iostream>
    #include <limits>
    #include <string>
    using namespace std;
    
    int demander_nombre(int min, int max);
    int Fibonacci(int n);
    int FibonacciIteratif(int n);
    
    // ----------------------------------------------------------------------
    int main()
    {
      char rep;
    
      do {
        int n(demander_nombre(0, 40));
        cout << "    F(" << n << ") = " << FibonacciIteratif(n)
             << endl;
        cout << "    F(" << n << ") = " << Fibonacci(n) << endl;
    
        do {
          cout << "Voulez-vous recommencer [o/n] ? ";
          cin >> rep;
        } while ((rep != 'o') and (rep != 'n'));
      } while (rep == 'o');
    
      return 0;
    }
    
    /* ----------------------------------------------------------------------
     * Sortie : F(n)
     * ---------------------------------------------------------------------- */
    int FibonacciIteratif(int n)
    {
    
      for (int i(1); i <= n; ++i) {
        Fn   = Fn_1 + Fn_2;    // pour n>=1 on calcule F(n)=F(n-1)+F(n-2)
        Fn_2 = Fn_1;           // et on decale...
        Fn_1 = Fn;
      }
      return Fn;
    }
    
    /* ----------------------------------------------------------------------
     * Sortie : F(n)
     * ---------------------------------------------------------------------- */
    int Fibonacci(int n)
    {
      if (n == 0)
        return 0;
      else if (n == 1)
        return 1;
      else
        return Fibonacci(n-1) + Fibonacci(n-2);
    }
    
    /* --------------------------------------------------------------
     * si b < a.
     * Sortie : le nombre saisi par l'utilisateur
     * -------------------------------------------------------------- */
    int demander_nombre(int a, int b)
    {
        int res;
    
        do {
          cout << "Entrez un nombre int ";
          if (a >= b)
          else
    	cout << "compris entre " << a << " et " << b;
          cout << " : ";
          cin >> res;
          if (cin.fail()) { 
    	     << "pas du charabia !" << endl;
            cin.clear();
    	// "jette" toute la ligne
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
    	res = a-1;
          }
        } while ((res < a) or ((a < b) and (res > b)));
    
       return res;
    }
    

    Exercice 4 : Calcul approché d'une intégrale (niveau 2)

    Exercice n°15 (pages 33 et 214) de l'ouvrage C++ par la pratique.
    #include <iostream>
    #include <cmath>
    using namespace std;
    
    double f(double x);
    double integre(double a, double b);
    double demander_nombre();
    
    int main()
    {
      double a(demander_nombre());
      double b(demander_nombre());
    
      cout.precision(12);
    
      cout << "Integrale de f(x) entre " << a
           << " et " << b << " :" << endl;
      cout << integre(a,b) << endl;
      return 0;
    }
    
    double f(double x) { return x*x; }
    // double f(double x) { return x*x*x ; }
    // double f(double x) { return 1.0/x ; }
    // double f(double x) { return sin(x); }
    
    double integre(double a, double b)
    {
      double res;
      res =  41.0 * ( f(a) + f(b) )
          + 216.0 * ( f((5*a+b)/6.0) + f((5*b+a)/6.0) )
          +  27.0 * ( f((2*a+b)/3.0) + f((2*b+a)/3.0) )
          + 272.0 * f((a+b)/2.0) ;
      res *= (b-a)/840.0;
      return res;
    }
    
    double demander_nombre()
    {
      double res;
      cin >> res;
      return res;
    }
    

    Exercice 5 : Fonctions logiques

    On peut remarquer que A ET A = A. NON(A ET A) vaut donc NON(A). On peut alors écrire la fonction non ainsi :

    bool non(bool A)
    {
      return non_et(A, A);
    }
    

    Comme NON(NON(C)) = C, on a NON(NON(A ET B)) = A ET B. On peut donc écrire la fonction et ainsi:

    bool et(bool A, bool B)
    {
      return non(non_et(A, B));
    }
    

    NON(A OU B) = NON(A) ET NON(B). Donc, (A OU B) = NON(NON(A OU B)) = NON(NON(A) ET NON(B)). On peut donc écrire la fonction ou ainsi:

    bool ou(bool A, bool B)
    {
      return non_et(non(A), non(B))
    }
    

    Ces relations sont intéressantes en électronique: il suffit d'un seul type de composant (effectuant la fonction non_et) pour réaliser n'importe quel circuit logique.


    Exercice 6 : recherche dichotomique

    Exercice n°34 (pages 83 et 256) de l'ouvrage C++ par la pratique.

    Aucune difficulté majeure pour cet exercice qui est niveau 2 uniquement parce que l'énoncé est moins détaillé.

    Voici le code correspondant :

    #include <iostream>
    #include <limits>
    using namespace std;
    
    int demander_nombre(int min, int max);
    unsigned int cherche(unsigned int borneInf, unsigned int borneSup);
    
    constexpr unsigned int MIN(1);
    constexpr unsigned int MAX(100);
    
    // ----------------------------------------------------------------------
    int main()
    {
           << endl << endl;
      const unsigned int solution( cherche(MIN, MAX) );
           << endl;
      return 0;
    }
    
    /* ----------------------------------------------------------------------
     * Recherche d'un nombre par dichotomie dans un intervalle [a b]
     * Sortie : la solution
     * ---------------------------------------------------------------------- */
    unsigned int cherche(unsigned int a, unsigned int b)
    {
      //  cout << "[ " << a << ", " << b << " ]" << endl;
    
      if (b < a) {
        return b;
      }
    
      unsigned int pivot((a+b)/2);
      char rep;
    
      do {
        cin >> rep;
      } while ((rep != '=') and (rep != '<') and (rep != '>'));
    
      switch (rep) {
      case '=':
        return pivot;
      case '<':
        return cherche(a, pivot-1);
      case '>':
        return cherche(pivot+1, b);
      }
    }
    

    Le seul petit truc auquel il faut penser est peut être le switch : oui ! on peut faire un switch sur un caractère.


    Exercice 7 : Recherche approchée de racine

    #include <iostream>
    #include <cmath>
    using namespace std;
    
    double const epsilon(1e-6);
    
     double f(double x) { return (x-1.0)*(x-1.5)*(x-2.0); }
    
     double df(double x) { return (f(x+epsilon)-f(x))/epsilon; }
    
     double itere(double x) { return x - f(x) / df(x); }
    
    int main()
    {
      double x1,x2;
      cin >> x2;
      
      do {
        x1 = x2;
        cout << "  au point " << x1 << " : "<< endl;
        cout << "    f(x)  = " << f(x1) << endl;
        cout << "    f'(x) = " << df(x1) << endl;
        x2 = itere(x1);
        cout << "    nouveau point = " << x2 << endl;
      } while (abs(x2-x1) > epsilon);
    
      cout << "Solution : " << x2 << endl;
    
      return 0;
    }
    

    Dernière mise à jour : $Date: 2014-02-17 14:41:41 +0100 (lun 17 fév 2014) $  ($Revision: 211 $)