Partage

[Exercices] Venez vous entraîner !

Ce mois: Parseur de fonctions mathématiques

2 mai 2008 à 19:23:19

nanoc, j'ai les conversion de chaines-> GDentier et
Gdentier -> chaines
J'ai aussi le produit mais pas l'addition, je comptais inclure ces fonctions dans la zérolib.
Ca me fait marrer qu'il y a ca juste au moment ou je commençais le projet.

Tu n'aurais pas piqué mon idée?
3 mai 2008 à 11:11:47

Citation : Jaloyan1

Tu n'aurais pas piqué mon idée?



Ce genre de classes est présente dans à peu près toutes les bibliothèques mathématiques... et dans tous les cours de programmation OO. T'as pas inventé la poudre...
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
3 mai 2008 à 12:27:10

Citation : Nanoc

Citation : Jaloyan1

Tu n'aurais pas piqué mon idée?



Ce genre de classes est présente dans à peu près toutes les bibliothèques mathématiques...




d'accord j'ai rien dit.

Mais c'est comme par hasard au moment ou j'ai commencé a travailler ce projet et que j'ai quelques bases que cet exercice est apparu.
Ca fait un peu bizarre.
4 mai 2008 à 13:54:36

Citation : Jaloyan1

Tu n'aurais pas piqué mon idée?



comme j'ai dit c'Est pratiquement un des premier TP d'un de mes ami a l'université alors oui c'Est tres courant comme exercisse
4 mai 2008 à 15:07:07

Solution du mois d'avril



Bonjour tout le monde ! Il est temps que je dévoile une solution pour l'exercice du mois d'avril. Vous avez été 20 à m'envoyer une solution. Parmis celles-ci seul 15 compilaient et 9 fonctionnaient correctement. Cependant, aucune des solutions proposées me satisfaisaient pleinement. La plus part étant bien trop complexes.

Je vous fais donc part de ma solution.

Partie "Papier-Crayon"



Cette partie est la plus essentielle dans tout le processus de résolution du problème. Prenons donc un cas concret et étudions la manière de procéder que devra avoir notre programme final.

Choisisson comme point de départ, une longueur maximale pour une ligne de 15 caractères et une phrase, par exemple "Salut les zéros! Comment allez-vous ?"

On peut découper le programme en 3 étapes:
  • Récupérer la phrase qu'a entré l'utilisateur
  • Découper la phrase en plusieurs lignes
  • Afficher ces lignes en entourant le tout de la "bulle"

La première étape ne pose pas de problème d'algorithme, je vous propose donc de passer à la deuxième qui est bien plus intéressante.

On veut découper la phrase en le moins de lignes possibles, il faut donc chercher le dernier espace dans la chaîne qui se trouve avant la limite des 15 caractère. Dans notre cas, il s'agit de l'esapce entre "les" e "zéros".

Salut les_zéros! Comment allez-vous ?

puisque l'espace suivant (entre le ! et "Comment") se trouve en 17ème position, ce qui est trop loin. En procédant de la sorte à partir de cet espace, on arrive finalement au découpage suivant:

Salut les_zéros! Comment_allez-vous ?_

A partir de là, il est aisé de découper cette phrases en lignes de la bonne longueur en remplacant les espaces rouges par des retours à la ligne.

Salut les
zéros! Comment
allez-vous ?

On a ainsi terminé le découpage de la phrase. Il ne nous reste plus que la troisième étape. Pour afficher le message, il faut alors distinguer les 2 cas possibles :
  • Le message fait une seule ligne
  • Le message fait plusieurs lignes

Dans le premier cas, il nous suffit donc d'afficher le message entouré des "<" et ">" comme dans l'exemple initial. Dans le deuxième cas, c'est plus complqué. Il faut chercher la ligne la plus longue pour fixer la taille de la bulle. Ceci se fait en cherchant la plus grande "distance" entre 2 retours à la ligne. Dans notre cas, c'est entre le 1er et le deuxième, la plus longue ligne fait donc 14 caractères. Ce qui fixe la largeur de la bulle à afficher.


Choix des structures de données



Il faut maintenant choisir quelle sera la structure de donnée la plus adaptée pour contenir la chaîne de caractère. En C++, la question ne se pose même pas, c'est le type std::string qu'il faut utiliser.

On peut tout de même faire un tour dans la documentation pour voir si il y a des fonctions membres qui pourraient nous être utiles. Par exemple ici: www.cplusplus.com (en anglais).

Parmi les fonctions qui pourraient nous être utiles, on peut trouver:

size() Renvoit la longueur de la chaine
find() Cherche un caractère et renvoit sa position
rfind() Cherche un caractère depuis la fin et renvoit sa position
replace() Remplace un suite de caractères à une position donnée par un autre
insert() Insère un caractère à une position donnée


Ceci devrait nous permettre de nous en sortir au vu de l'algorithme que nous avons développé dans la partie précédente.

Pour ce qui est de l'afficage d'une bulle de taille fixe, je vous propose une solution un peu plus avancée que de compter "bêtement" les espaces, il s'agit des manipulateurs de flux. Je vous invite à suivre ce lien si vous ne connaissez pas cet aspect du langage.

Programmation



A partir de ce qui a été développé précédemment, voici le code que l'on pourrait écrire :

#include <iostream>
#include <string>
#include <iomanip>

using namespace std;

void decoupe(string&);
// Decoupe une chaine de caractères en lignes de LARGEUR_MAXIMUM caractères au maximum
// Essaye de découper au niveau des espaces si il y en a

void affiche(string&);
// Affiche Une chaîne de caractères dans une bulle de taille fixee par la plus longue ligne à afficher.

const unsigned int LARGEUR_MAXIMUM = 40;  //Largeur maximale acceptable pour une bulle
const string LAPIN = "   \\\n    \\\n    (\\(\\\n    ( - -)\n    ((') (')\n"; //Le ptit pin-pin


int main(int argc, char *argv[])
{
    if (argc < 2) //On affiche un message d'erreur si l'utilisateur n'a pas donne d'argument
    {
        cout << "Le programme s'utilise de la maniere suivante :\n" << argv[0] << " \"phrase a afficher\"" << endl;
        return 0;  //et on termine le programme
    }

    string phrase=argv[1];  //On declare une chaine que l'on remplit avec l'argument

    //On récupère tous les arguments passés pour le cas ou l'utilisateur a oublie les ""
    for (int i = 2; i < argc; ++i)
        phrase = phrase + " " + argv[i];

    //On decoupe la phrase
    decoupe(phrase);

    //et on l'affiche
    affiche(phrase);

    return 0;
}

void decoupe(string& chaine)
{
    //On declare 2 variables utiles pour le travail a effectuer
    unsigned int debut(0);   //Position du debut d'une nouvelle ligne
    unsigned int fin;        //Position de la fin de la nouvelle ligne

    while ( debut + LARGEUR_MAXIMUM < chaine.size() )  //Tant qu'on a pas atteint la dernier ligne
    {
        // on cherche le dernier espace de la chaine se trouvant entre le
        // debut de la ligne actuelle + la largeur maximale de la bulle
        // On stocke la position dans 'fin' pour savoir ou se termine cette ligne
        fin = chaine.rfind(" ", debut + LARGEUR_MAXIMUM);

        //Si il n'y a pas d'espace dans la zone ou on a cherche
        if ( fin == string::npos )
        {
            // Alors on insère un retour à la ligne de maniere a
            // ne pas depasser hors de la limite de la bulle
            chaine.insert(debut + LARGEUR_MAXIMUM, "\n");
            //Et on passe a la ligne suivante
            debut += LARGEUR_MAXIMUM+1;  //+1 car il y a le \n
        }
        else  //Si par contre il y avait un espace dans la zone recherchee
        {
            // on remplace l espace trouvé par un retour a la ligne
            chaine.replace(fin,1,"\n");
            //Et on passe a la ligne suivante
            debut = fin +1;
        }
    }

}

void affiche(string& texte)
{
    //On commence par chercher le nombre de lignes du texte a afficher
    unsigned int nombre_lignes(1);

    for (unsigned int i(0); i<texte.size();++i)
        if (texte[i] == '\n')	//Chaque fois qu'il y a un retour a la ligne
            ++nombre_lignes;    //C'est qu'il y a une ligne supplementaire


    if (nombre_lignes == 1) //Si il n'y a qu'une ligne
    {
        cout <<" " << setw(texte.size()+2) << setfill('_') <<'_' << " " << endl;   //haut de la bulle
        cout <<"< " << setw(texte.size()) << setfill(' ') << texte <<" >" << endl; //texte
        cout <<" " << setw(texte.size()+2) << setfill('-') <<'-' << " " << endl;   //bas de la bulle
        //Le +2 pour mettre des espaces au debut et a la fin de la ligne

        cout << LAPIN;  //et le petit lapin
        return;
    }

    //Si on arrive la c'est qu'il y a plusieurs lignes
    //il faut donc chercher la longueur de la plus grande ligne

    unsigned int longueur_lignes(0);

    for (unsigned int i(0), pos_endl(0);i<=texte.size();++i)  //pos_endl = position du dernier retour a la ligne
    {
        if (texte[i] == '\n' || i==texte.size()-1)  //Si c'est un retour a la ligne ou qu'on a atteint la fin de la chaine
        {
            if (i-pos_endl > longueur_lignes)  //et que la distance depuis le dernier retour est plus grande que avant
                longueur_lignes = i-pos_endl;  //On garde cette distance comme etant la plus grande

            pos_endl =i;  //et on met a jour la position du dernier retour a la ligne
        }
    }

    //L'affichage est ici en 5 partie, le dessus, la 1ere ligne, le milieu, la derniere ligne et le dessous
    //Le texte de chaque ligne est constitue de la sous-chaine partant du debut et allant jusqu'au premier \n (non-compri)
    //On detruit ensuite ce meme debut + le \n
    //Et on recommence pour la ligne suivante en modifiant les debuts et fins de ligne pour la bulle

    cout <<" " <<left << setw(longueur_lignes+2) << setfill('_') <<'_' << " " << endl;  //On affiche le haut de la bulle

    int pos = texte.find('\n');  //On cherche le premier retour a la ligne
    cout <<"/ " << setw(longueur_lignes) << setfill(' ') << texte.substr(0,pos) << " \\" << endl;  //On affiche la premiere ligne
    texte.erase(0,pos+1);

    for (unsigned int i(1);i<nombre_lignes-1;++i)  //On affiche les lignes 2 à n-1
    {
        pos = texte.find('\n');
        cout <<"| " << setw(longueur_lignes) << setfill(' ') << texte.substr(0,pos) << " |" << endl;
        texte.erase(0,pos+1);
    }

    pos = texte.find('\n');
    cout <<"\\ " << setw(longueur_lignes) << setfill(' ') << texte.substr(0,pos) << " /" << endl;  //On affiche la derniere ligne

    cout <<" " << setw(longueur_lignes+2) << setfill('-') <<'-' << " " << endl;  //On affiche le bas de la bulle

    cout << LAPIN;  //Et on affiche le lapin
}


Il me semble que les commentaires situés dans le code devrait permettre de comprendre les étapes un peu plus complexes de l'algorithme proposé tout au début.

J'ai choisi d'afficher un lapin pour changer un peu. Voici autrement le Zozor de Bombadil :

cout << "     \\   _      _                             " << endl;
cout << "      \\  \\____ /                                " <<endl;
cout << "         (O  o)      _                         " <<endl;
cout << "         /    \\_____|                          " <<endl;
cout << "        (@____@)    )                          " <<endl;
cout << "         (uuuu)____ )                          " <<endl;
cout << "         (uuuu) [] []                          " <<endl;
cout << "          [] []                                " <<endl;


Remarques sur les codes reçus



  • Beaucoup de monde n'avait pas fait attention que la bulle devait adapter sa taille si le texte est plus court qu'une certaine taille maximale
  • Evitez autant que possible les char* en C++ ! Les string sont beaucoup mieux.
  • Pour un exercice de ce type, il est inutile de faire 3 namespaces et plus de 500 lignes de code...
  • Evitez la fonction exit(), elle n'est à utiliser qu'en dernier recours.
  • Evitez les #define pour faire des constantes, préférez le mot-clé const.
  • En C++, pas besoin d'inclure la cstdlib.


Il ne sert à rien d'envoyer votre code si il ne fonctionne pas ou pire, si il ne compile pas.


Merci à tous ceux qui ont participé. Et bonne chance avec l'exercice suivant !
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
4 mai 2008 à 15:55:59

Merci Nanoc :)

Tu as mon erreur de ma fonction découpe '^^ que j'avais pas renvoyé pour ce détail.
il faut remplacer
//Si il n'y a pas d'espace dans la zone ou on a cherche
        if ( fin == string::npos )

par
//Si il n'y a pas d'espace dans la zone ou on a cherche, ou alors qu'on tombe sur l'espace d'avant parce que la nouvelle ligne est trop grande et ne contient pas d'espace
        if ( fin == string::npos || fin < debut )


Pour comprendre, avec l'ancien code, réduis la taille max à 10, et affiche le texte "je suis un essaiessaiessai" ;)
Anonyme
4 mai 2008 à 16:00:43

Tout d'un coup je comprends pourquoi mon code était long. >_<
Merci pour cette correction éclairante!
4 mai 2008 à 16:39:08

Disons qu'il était plus long que la moyenne :)
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
4 mai 2008 à 17:50:49

phrase = phrase + " " + argv[i];
Hum, pourquoi ne pas utiliser += ?
4 mai 2008 à 20:06:45

Essaies pour voir^^

Quand tu fais string += char* + char*, cela fait en réalité:

string += (char* + char*).

Or l'opérateur + entre 2 char* n'est pas défini. C'est une question de priorité des opérateurs. On est obligé de faire :

string = string + char* + char*

qui se lit comme :

string = (string + char*) + char*
string = (string + char*)
string = string

Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
4 mai 2008 à 20:31:49

bah phrase += string(" ") + argv[i] alors :-'
4 mai 2008 à 21:15:40

suffit de faire:
( string += char* )+ char*;
;)
4 mai 2008 à 22:44:19

"( string += char* )+ char*;"
Marchera pas comme attendu, contrairement à la solution de devy.
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
4 mai 2008 à 23:06:27

ça marchera si operator+= retournait &this ;)
4 mai 2008 à 23:46:05

Cela ne sert à rien. La valeur est perdue.
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
5 mai 2008 à 0:40:19

ah bon? je dormirai moin con :)
5 mai 2008 à 1:21:31

+() ne modifie pas ses arguments. +=(). +=() pourrait donc être utilisé en chaine.
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
5 mai 2008 à 19:41:44

J'ai pas rendu mon code a temps pour manque de temps mais merci pour cette correction trés instructive.
Anonyme
6 mai 2008 à 20:34:06

Juste une petite question. Pas sur cet exo.
Je me demandais si tu nous proposerais "que" des exercices où on doit construire le programme, ou si tu nous proposeras d'autre style d'exo. [ tel que http://www.gotw.ca/gotw/002.htm ]
Voilà, merci. :)
6 mai 2008 à 20:38:13

Pour le moment j'ai assez d'idées différentes d'exercices pour tenir assez longtemps.
Il risque aussi d'en avoir du type ou il faut coder les parties manquantes d'un code pour terminer un algorithme.

Pour ce qui est de Guro of the week, je trouve qu'ils sont souvent très technique et demandent une connaissance pointue de certains trucs. De plus je n'ai pas la prétention de faire du code de ce niveau.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
Anonyme
6 mai 2008 à 21:16:12

D'accord! Me réjouis déjà. :)

PS : l'exemple gotw était simplement pour illustrer une autre sorte d'exo [ correction ].
8 mai 2008 à 11:29:02

Une petite question :

Si on a un problème avec un exo, peut-on poster un topic sur le site pour résoudre son problème ?
8 mai 2008 à 11:53:55

Citation : gymnopaul

Une petite question :

Si on a un problème avec un exo, peut-on poster un topic sur le site pour résoudre son problème ?


Bien sûr. Mais crée un nouveau topic, tu auras plus de chance d'avoir de l'aide et ça laisse celui-ci plus clair.
11 mai 2008 à 20:31:36

Mmmh.... moins de réponses ce mois pour le moment. C'est vrai que c'est plus compliqué et qu'il y a plus à faire.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
12 mai 2008 à 9:18:30

désolé mais moi je ne pourrai pas, j'ai trop de travail a l'école.
12 mai 2008 à 12:05:16

Bonjour,

Projets a rendre + Partiels pour ma part... Je ne pense pas que je pourrais rendre quelque chose ce mois ci. Bon courage au participants !
Inkamath on GitHub - Interpréteur d'expressions mathématiques. Reprise du développement en cours.
Anonyme
12 mai 2008 à 14:05:06

Je ne réaliserais pas cet exo. Pourquoi? Je n'ai pas les outils [ mathématiques du moins ] pour créer une vraie classe BigInt. [ voir remarque de lmghs / Nanoc. ] Et je n'aime pas faire de "mauvaises" choses. :-°
12 mai 2008 à 14:24:51

Les outils nécessaires sont ceux que tu as appris au collège. Maintenant si le but est de réaliser une classe effectivement applicable dans un programme de simulation numérique type Matlab ou Octave, alors ne comptez pas sur moi. Je n'en ai pas le niveau et pas la prétention de le croire.

Ce n'est pas mauvais de réaliser des choses plus simples. C'est comme la classe ZString de M@teo21, elle est mauvaise comparée à ce qu'on trouve dans la STL, elle est cependant très insturctive.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
12 mai 2008 à 14:40:13

tu as quel age hiura ? et dans quel niveau scolaire?