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

Correction
SDA & Bibliothèques

Exercice 0 Exercice 1 Exercice 2 Exercice 3

Exercice 0 : piles et parenthèses

Exercice n°39 (pages 91 et 274) de l'ouvrage C++ par la pratique.

Voici une version avec les piles du standard C++, les stacks :

#include <iostream>
#include <stack>
#include <string>
using namespace std;
 
typedef char type_el;
 
// vérification de parenthèsage
bool check(const string& parentheses);
 
//----------------------------------------------------------------------
int main()
{
  string s;
 
  do {
    cout << "Entrez une expresssion parenthèsée : ";
    getline(cin, s);
    if (!s.empty())
      cout << " -> ";
      if (check(s)) {
        cout << "OK";
      } else {
        cout << "Erreur";
      }
      cout << endl;
  } while (!s.empty());
  return 0;
}
 
//----------------------------------------------------------------------
bool check(const string& s) {
  stack<type_el> p;
  for (auto c : s) {
    if ((c == '(') or (c == '[')) {
      p.push(c);
    } else if (c == ')') {
      if ((not p.empty()) and (p.top() == '(')) p.pop();
      else return false;
    } else if (c == ']') {
      if ((not p.empty()) and (p.top() == '[')) p.pop();
      else return false;
    }
  }
 
  return p.empty();
}
 

Exercice 1 : piles et notation polonaise inverse

Exercice n°41 (pages 94 et 281) de l'ouvrage C++ par la pratique.

Protoype :

int eval(string entree);

ou si l'on veut éviter la copie du passage par valeur, on peut optimiser en :

int eval(const string& entree);

Définition : suivre l'algorithme donné ne devrait pas présenter de difficulté.

#include <iostream>
#include <iomanip>
#include <stack>
#include <string>
using namespace std;
 
typedef int type_el;
 
int eval(const string& entree);
 
//----------------------------------------------------------------------
int main()
{
  string s;
  do {
    cout << "Entrez une expresssion à évaluer : ";
    getline(cin, s);
    if (not s.empty()) 
      cout << " -> résultat : " << eval(s) << endl;
  } while (not s.empty());
  return 0;
}
 
//----------------------------------------------------------------------
int eval(const string& s) 
{
  stack<type_el> p;
 
  // recopie dans la pile
  for (auto c : s)
    if ((c >= '0') and (c <= '9')) {
      // On a lu un chiffre
      p.push(c - '0');
 
    } else if ((c == '+') or (c == '-') or 
	       (c == '*') or (c == '/')) {
 
      // recupere le second argument
      int y(p.top());
      p.pop();;
 
      // recupere le premier argument
      int x(p.top());
      p.pop();;
 
      // calcule et empile le resultat
      switch(c) {
      case '+': p.push(x + y); break;
      case '-': p.push(x - y); break;
      case '*': p.push(x * y); break;
      case '/': p.push(x / y); break;
      }
    }
 
  return p.top();
}
 

Exercice 2 : Ensembles et itérateurs

Exercice n°66 (pages 171 et 365) de l'ouvrage C++ par la pratique.

Pour pouvoir créer un ensemble, il faut avant tout utiliser la bibliothèque correspondante. On met donc :

#include <set>

Un ensemble de caractères se déclare simplement ensuite par :

  set<char> unensemble;

Pour insérer des éléments, on utilise la méthode insert :

  unensemble.insert('a');
  unensemble.insert('b');
  unensemble.insert('c');
 

Si l'on souhaite maintenant parcourir cet ensemble, il nous faut un itérateur pour cette classe "ensemble". Il se déclare par set<char>::iterator.

Si on veut partir du début de l'ensemble, cela correspond pour l'itérateur à la valeur retournée par la méthode begin() de la classe set (comme tout container de la STL).

On aura donc :

set<char>::iterator i(unensemble.begin())

De même, la valeur (suivant la) finale correspond à unensemble.end(), et pour faire avancer un itérateur on peut utiliser l'opérateur ++ comme pour les nombres et pour les pointeurs.

L'accès à la valeur du container désignée par un itérateur, par exemple i, se fait en utilisant l'opérateur * : *i

On aboutit alors à :

  cout << "mon ensemble contient :" << endl;
  for (set<char>::iterator i(unensemble.begin()); 
       i != unensemble.end(); ++i) {
    cout << "    " << *i << endl;
  }
 

En C++11, on aurait aussi pu, plus simplement écrire :

  cout << "mon ensemble contient :" << endl;
  for (auto const& e : unensemble) {
    cout << "    " << e << endl;
  }
 

mais le but premier de cet exercice est de travailler les itérateurs ;-).

Ensuite, pour copier un container (par exemple set) dans un autre (par exemple string), il est impératif que le second ait au moins la place pour accueillir la copie, c.-à-d, ait au moins la taille du container copié.

Dans notre cas on crée donc une string avec au moins 3 caractères : string s("abc");

La copie s'effectue alors simplement avec l'algorithme générique de copie :

  copy(unensemble.begin(), unensemble.end(), s.begin());

La copie sur la sortie cout se fait de même, sauf qu'il faut "transformer" le flot cout en un itérateur ; ce qui se fait avec [niveau avancé] ostream_iterator<char>(cout, ", "), où le dernier argument représente le séparateur utilisé entre chacun des éléments du container affichés.

Voici, pour finir, le code complet :

#include <iostream>  // pour cout
#include <algorithm> // pour copy
#include <set>
#include <string>
#include <iterator>  // pour ostream_iterator
using namespace std;
 
int main()
{
  set<char> unensemble;
 
  unensemble.insert('a');
  unensemble.insert('b');
  unensemble.insert('c');
 
  cout << "mon ensemble contient :" << endl;
  for (set<char>::iterator i(unensemble.begin()); 
       i != unensemble.end(); ++i) {
    cout << "    " << *i << endl;
  }
 
  string s;
 
  // pour utiliser copy, il est impératif que la chaîne ait
  // la place nécessaire, donc ici au moins 3 caractères.
  // En pratique on utilisera donc pas copy pour CREER un
  // nouveau container, mais bien pour COPIER des valeurs;
  s = "xxx";
 
  copy(unensemble.begin(), unensemble.end(), s.begin());
 
  cout << s << endl;
 
  // affichage
  cout << "mon ensemble = ";
  copy(unensemble.begin(), unensemble.end(), 
       ostream_iterator<char>(cout, ", "));
  cout << endl;
 
  return 0;
}
 

Exercice 3 : tris

Exercice n°67 (pages 171 et 367) de l'ouvrage C++ par la pratique.

L'exercice est suffisamment simple, je crois, pour que la solution se passe de commentaires.
Voici une solution possible :

#include <vector>
#include <algorithm> // pour sort()
#include <random>
#include <functional> // pour bind()
#include <iostream>
#include <iterator>  // pour ostream_iterator
using namespace std;
 
// Un tableau de valeurs.
typedef int Valeur;
typedef vector<Valeur> Tableau;
 
void affiche(Tableau t) {
  copy(t.begin(), t.end()-1, ostream_iterator<Valeur>(cout, ", "));
  cout << t.back() << endl;
}
 
int main()
{
  // choix de la graine
  random_device rd; // utilisé ici pour tirer la graine au hasard
  unsigned int graine(rd()); 
 
  // choix du générateur et initialisation (graine)
  default_random_engine generateur(graine);
 
  // distribution uniforme entre -5 et 5
  uniform_int_distribution<Valeur> distribution(-5, 5);
 
  // pour les tirages
  auto tirage(bind(distribution, generateur));
 
  // un tableau de, disons 25, valeurs à trier.
  Tableau tableau(25);
 
  for (auto& element : tableau) {
    element = tirage();
  }
 
  cout << "avant le tri :" << endl;
  affiche(tableau);
 
  // trie le tableau
  sort(tableau.begin(), tableau.end());
 
  cout << endl << "après le tri :" << endl;
  affiche(tableau);
 
  return 0;
}
 

Dernière mise à jour : $Date: 2012-11-09 14:16:46 +0100 (ven 09 nov 2012) $  ($Revision: 191 $)