Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exercices] Venez vous entraîner !

(v2)

    1 mai 2012 à 14:24:46

    Pourri comme exo ...
    Je suis brut mais bon ... C'est claire que c'est très simple à coder, aucun choix niveau algorithmique important (au pire, on peut faire de l'obfuscation, mais ce serrait même pas drôle).
    Aucun intérêt, ... Ce n'est même pas du C++ pur mais plus du C++/Qt.
    • Partager sur Facebook
    • Partager sur Twitter
    🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles  - ♡ Copying is an act of love.
      1 mai 2012 à 14:37:37

      Ok.

      Je tiens à préciser que cet exo s'adresse aux Débutants !


      Améliorations possibles
      • -Faire en sorte que l'on puisse vraiment envoyer des mails
      • -Demander le pseudo (Site du Zéro) de l'utilisateur et lui envoyer un message via le Site, et si impossible, refuser l'accès.
      • -Demander à l'utilisateur sur quel site veut-il aller, et créer le lien adapté

      • Partager sur Facebook
      • Partager sur Twitter
      HEY ! CETTE SIGNATURE EST INUTILE !
      Anonyme
        1 mai 2012 à 20:49:43

        Citation : @che

        Pourri comme exo ...
        Je suis brut mais bon ... C'est claire que c'est très simple à coder, aucun choix niveau algorithmique important (au pire, on peut faire de l'obfuscation, mais ce serrait même pas drôle).
        Aucun intérêt, ... Ce n'est même pas du C++ pur mais plus du C++/Qt.

        • Partager sur Facebook
        • Partager sur Twitter
          19 juin 2012 à 14:11:40

          Bonjour ! Je me suis mis à travailler sur l'exercice "Site du Zéro" (polymorphisme). J'ai remarqué ceci dans la présentation de Nanoc.

          Citation : Nanoc

          Dois-je utiliser l'héritage public ou privé ?



          Tout dépend si la classe fille EST UNE classe mère ou si elle EST IMPLÉMENTÉE SOUS FORME DE classe mère.



          Je ne comprend pas en effet, quelle différence y a-t-il entre l'héritage privé et l'héritage public ?
          • Partager sur Facebook
          • Partager sur Twitter
            19 juin 2012 à 14:19:16

            l'héritage privé s'apparente très souvent à un attribut dont on implémente les fonctions virtuelles ;)
            • Partager sur Facebook
            • Partager sur Twitter
              19 juin 2012 à 14:57:26

              @Hellish, ouvre un nouveau fil pour discuter de cela STP.
              On y répondra dessus.
              • Partager sur Facebook
              • Partager sur Twitter
              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.
                23 juin 2012 à 19:43:27

                Ma solution à l'exercice de l'interpréteur Befunge



                Voilà voilà, rien d'autre à faire alors je vous propose mon petit interpréteur Befunge :)

                Le code doit être écrit dans un fichier, lequel, passé en paramètre du main, sera interprété. Je n'ai pas voulu ajouter de fonctionnalités supplémentaires, mon interpréteur gère toutes les instructions et propriétés (programme toroïdale, etc.) décrites sur la page wikipédia. À noter que la taille de la grille n'est pas fixée, elle s'adapte au fichier et peut donc être arbitrairement grande.

                Le code :
                #include <iostream>
                #include <fstream>
                #include <string>
                #include <vector>
                #include <stdexcept>
                #include <stack>
                #include <ctime>
                #include <boost/random/mersenne_twister.hpp>
                #include <boost/random/uniform_int.hpp>
                
                #define DIGIT  '0' : case '1' : case '2' : case '3' : case '4' \
                        : case '5' : case '6' : case '7' : case '8' : case '9'
                
                class Befunge
                {
                    std::vector < std::string > code;
                
                    int i, j;
                    bool ascii_mode;
                    enum Dir { Up, Down, Left, Right } direction;
                    std::stack < int > mem;
                
                    boost::random::mt19937 gen;
                    boost::random::uniform_int_distribution < int > dist;
                
                    // la fonction suivante s'assure que le programme est bien un rectangle et l'ajuste sinon
                    void to_rectangle()
                    {
                        std::size_t max_size = 0;
                        for(std::size_t i = 0; i < code.size(); ++i)
                            if(code[i].size() > max_size)
                                max_size = code[i].size();
                
                        for(std::size_t i = 0; i < code.size(); ++i)
                            code[i] += std::string(max_size - code[i].size(), ' ');
                    }
                
                    // calcule l'emplacement de la prochaine instruction
                    void next_instr()
                    {
                        switch(direction)
                        {
                            case Up :
                                --i; break;
                            case Down :
                                ++i; break;
                            case Left :
                                --j; break;
                            case Right :
                                ++j; break;
                        }
                    }
                
                    // Accès à une instruction (+ correction toroïdale si nécessaire)
                    char& access_instr(int& i_, int& j_)
                    {
                        // la grille du programme est en réalité un tore :
                        while(i_ < 0)
                            i_ = code.size() + i_;
                        while(i_ >= static_cast < int >(code.size()))
                            i_ -= code.size();
                        while(j_ < 0)
                            j_ = code[i_].size() + j_;
                        while(j_ >= static_cast < int >(code[i_].size()))
                            j_ -= code[i_].size();
                
                        return code[i_][j_];
                    }
                
                    // Les trois fonctions suivantes facilitent la récupération des valeurs en sommet de pile :
                    void get_mem(int& a)
                    {
                        a = mem.top(); mem.pop();
                    }
                
                    void get_mem(int& a, int& b)
                    {
                        get_mem(a);
                        get_mem(b);
                    }
                
                    void get_mem(int& a, int& b, int& c)
                    {
                        get_mem(a, b);
                        get_mem(c);
                    }
                
                  public :
                
                    Befunge(const std::vector < std::string >& code_)
                      : code(code_), dist(0, 3)
                    {
                        gen.seed(std::time(NULL));
                        to_rectangle();
                        init();
                    }
                
                    // On peut réinitialiser l'interpréteur, d'où une default-init en fonction distincte du constructeur
                    void init()
                    {
                        i = j = 0;
                        ascii_mode = false;
                        direction = Right;
                    }
                
                    void start() // À appeler pour lancer l'interprétation
                    {
                        char instr;
                        int a, b, c;
                
                        while(true) // boucle principale de l'interpréteur
                        {
                            instr = access_instr(i, j);
                
                            if(ascii_mode)
                            { // si mode ascii, empiler la valeur ASCII de n'importe quel caractère
                                if(instr == '"') // sauf '"' qui termine ce mode
                                    ascii_mode = false;
                                else mem.push(static_cast < int >(instr));
                            }
                
                            else switch(instr) // sinon, analyser et exécuter l'instruction
                            {
                                case ' ' : // continuer dans la même direction
                                    break;
                
                                case '@' : // fin du programme
                                    goto end; // Si vous vous apprêtez à critiquer cette ligne, lire http://blog.huoc.org/goto.html puis s'abstenir
                
                                case DIGIT :
                                    mem.push(instr - '0');
                                    break;
                
                                // Opérations élémentaires :
                                case '+' :
                                    get_mem(a, b);
                                    mem.push(a + b);
                                    break;
                
                                case '-' :
                                    get_mem(a, b);
                                    mem.push(b - a);
                                    break;
                
                                case '*' :
                                    get_mem(a, b);
                                    mem.push(a * b);
                                    break;
                
                                case '/' :
                                    get_mem(a, b);
                                    if(a == 0)
                                        std::cin >> c;
                                    else c = b / a;
                                    mem.push(c);
                                    break;
                
                                case '%' :
                                    get_mem(a, b);
                                    if(a == 0)
                                        std::cin >> c;
                                    else c = b % a;
                                    mem.push(c);
                                    break;
                
                                case '!' :
                                    get_mem(a);
                                    mem.push(!a);
                                    break;
                
                                case '`' :
                                    get_mem(a, b);
                                    mem.push(b > a);
                                    break;
                
                                // Changements de direction :
                                case '>' :
                                    direction = Right;
                                    break;
                
                                case '<' :
                                    direction = Left;
                                    break;
                
                                case '^' :
                                    direction = Up;
                                    break;
                
                                case 'v' :
                                    direction = Down;
                                    break;
                
                                case '?' :
                                    direction = static_cast < Dir >(dist(gen));
                                    break;
                
                                case '_' :
                                    get_mem(a);
                                    direction = (a == 0) ? Right : Left;
                                    break;
                
                                case '|' :
                                    get_mem(a);
                                    direction = (a == 0) ? Down : Up;
                                    break;
                
                                // Divers :
                                case '"' : // Démarre le mode ASCII (si on se trouve là, c'est qu'il est préalablement désactivé)
                                    ascii_mode = true;
                                    break;
                
                                case ':' : // duplique la valeur en sommet de pile
                                    get_mem(a);
                                    mem.push(a);
                                    mem.push(a);
                                    break;
                
                                case '\\' : // inverse les deux valeurs en sommet de pile
                                    get_mem(a, b);
                                    mem.push(a);
                                    mem.push(b);
                                    break;
                
                                case '$' : // supprime le sommet de pile
                                    mem.pop();
                                    break;
                
                                case '.' :
                                    get_mem(a);
                                    std::cout << a; // affichage en tant qu'entier ...
                                    break;
                
                                case ',' :
                                    get_mem(a);
                                    std::cout << static_cast < char >(a); // ... ou en tant que caractère ASCII
                                    break;
                
                                case '#' : // saute l'instruction suivante
                                    next_instr();
                                    break;
                
                                case 'p' : // modifier l'instruction à (a, b) en ASCII(c)
                                    get_mem(a, b, c);
                                    access_instr(a, b) = static_cast < char >(c);
                                    break;
                
                                case 'g' : // empile le code ASCII de l'instruction à (a, b)
                                    get_mem(a, b);
                                    mem.push(static_cast < int >(access_instr(a, b)));
                                    break;
                
                                case '&' : // demande un nombre et l'empile
                                    std::cin >> a;
                                    mem.push(a);
                                    break;
                
                                case '~' : // demande un caractère c et empile ASCII(c)
                                    mem.push(static_cast < int >(std::getchar()));
                                    break;
                
                                default :
                                    throw std::logic_error(std::string("Unexpected instruction : ") + instr);
                            }
                
                            next_instr(); // charge la prochaine instruction
                        }
                
                     end:
                        return;
                    }
                };
                
                int main(int argc, char* argv[])
                {
                    if(argc < 2)
                    {
                        std::cerr << "[Befunge] Merci d'indiquer un fichier source\n";
                        return 1;
                    }
                
                    std::fstream file(argv[1], std::fstream::in);
                    std::vector < std::string > code;
                
                    // on récupère le code :
                    while(file.good())
                    {
                        std::string line;
                        std::getline(file, line);
                        code.push_back(line);
                    }
                
                    Befunge interpreter(code);
                
                    try {
                        interpreter.start(); // on lance l'interprétation
                    }
                    catch(const std::exception& e)
                    {
                        std::cerr << "[Befunge] Error : " << e.what();
                    }
                
                    return 0;
                }
                


                Quelques screen


                Générateur de nombres pseudo-aléatoires :

                Image utilisateur

                Une petite fractale :

                Image utilisateur

                Calcul d'une courbe sinusoïdale :

                Image utilisateur

                Et voici une petite liste de codes Befunge à interpréter. Amusez-vous ;)

                N'hésitez pas à critiquer / commenter mon code, on est là pour ça ! Si vous rencontrez un bug, merci de m'en faire part également.

                shareman
                • Partager sur Facebook
                • Partager sur Twitter
                  23 juin 2012 à 20:42:57

                  Bravo et merci de proposer ta solution (très propre) :) !
                  • Partager sur Facebook
                  • Partager sur Twitter
                    23 juin 2012 à 21:16:00

                    et très joli d'ailleur ^^ (je parle des exemples ^^ ) dommage que la fenètre soit bleue
                    • Partager sur Facebook
                    • Partager sur Twitter
                    Anonyme
                      23 juin 2012 à 22:32:02

                      Très bonne correction... Vraiment très propre, en plus.
                      C'est marrant, je bosse justement sur la création d'un "descendant" du Befunge en ce moment, et sur la création d'un "IDE"...

                      Et j'ai besoin d'aide. :-°
                      • Partager sur Facebook
                      • Partager sur Twitter
                        24 juin 2012 à 3:35:32

                        Merci ^^

                        Drôle de coïncidence Hugooguh, il y a peu de temps encore je m'étais lancé dans l'élaboration d'un tel IDE (avec les fonctionnalités que tu décris + exécution lente et didactique, "ouvrir" et "enregistrer", des petits trucs comme ça), mais la flemme a pris le dessus...
                        • Partager sur Facebook
                        • Partager sur Twitter
                          24 juin 2012 à 7:27:24

                          Je sais que je suis sensé m'abstenir, mais j'aime enfreindre les interdits : goto c'est mal, j'ai pas lu ton lien mais blablabla <insert troll>...
                          Plus sérieusement, pour ce que fait ton goto tu pouvais obtenir le même résultat en écrivant directement directement return dans le switch. On pouvais aussi s'en sortir avec un booléen pour vérifier que le programme continue de tourner.

                          Voilà en fait je n'ai pas grand chose à redire. Simplement je pense que je n'aurais pas laissé init en public pour cacher définitivement toute notion de données à l'utilisateur. Concrètement si l'on initialise l'interpréteur, c'est pour le relancer après, et inversement, une fois que le programme a tourné on a besoin de réinitialiser l'interpréteur avant de le relancer (les seuls moyens de sortir de la boucle d'interprétation sont soit de rencontrer le symbole marquant la fin de l'exécution, soit de déclencher une exception. Dans le premier cas cela ne fait aucun sens de relancer la machine telle quelle, dans le deuxième, on pourrait peut être le justifier, mais le code ne le permet de toutes façons pas).
                          Par conséquent je pense qu'il serait judicieux de mettre init en private et d'y faire appel au début de start.
                          L'utilisateur n'aura plus qu'à appeler start autant de fois qu'il veut faire tourner le programme, sans se soucier de la mécanique interne de la classe.

                          Edit : en fait il y a un dernier truc qui me chiffonne. Je n'aime pas l'idée de laisser à l'utilisateur le soin de remplir un tableau de std::string. Tant qu'à faire, encapsuler toute la partie chargement dans une classe (disons BefungeFile) allègerait le travail de l'utilisateur final. On pourrait en profiter pour déplacer to_rectangle, qui à mon avis n'est pas du ressort de Befunge, dans BefungeFile. Le main final ressemblerait à ça
                          int main(int argc, char* argv[])
                          {
                              if(argc < 2)
                              {
                                  std::cerr << "[Befunge] Merci d'indiquer un fichier source\n";
                                  return 1;
                              }
                          
                              try {
                                  BefungeFile code(argv[1]); //je suis parti du principe qu'on pouvais lancer une exception s'il y avait une erreur lors de l'ouverture/de la lecture du fichier source
                                  Befunge interpreter(code);
                                  interpreter.start(); // on lance l'interprétation
                              }
                              catch(const std::exception& e)
                              {
                                  std::cerr << "[Befunge] Error : " << e.what();
                              }
                          
                              return 0;
                          }
                          
                          J'ai d'autres idées qui me trottent dans la tête mais ce n'est pas encore bien clair et ça nécessiterait certainement de plus grosses modifications du code.
                          • Partager sur Facebook
                          • Partager sur Twitter
                          Anonyme
                            24 juin 2012 à 11:36:17

                            Pas faux. Et puis, tout compte fait, je trouve ça pas très bien, la non-protection contre les SEGFAULT... Si on a un programme avec juste un ':', ça plante ; moi, j'aurais mis une protection dans le code de get_mem() :
                            void get_mem(int& a)
                            {
                                if (mem.size())
                                {
                                    a = mem.top(); mem.pop();
                                }
                                else
                                    throw runtime_error(std::string("SegFault : empty pile when activating instruction '") + access_instr(i, j) + '\'');
                            }
                            


                            Sinon, t'as mis une licence pour ton code ? Je pourrais peut-être l'intégrer comme autre interpréteur pour mon IDE... ;) (bien sûr, tu serait cité comme génial auteur de ce chef-d'oeuvre)
                            Il ne manquerait plus que les breakpoints, et un accès aux données, et ça serait super.
                            • Partager sur Facebook
                            • Partager sur Twitter
                              24 juin 2012 à 11:38:50

                              Je n'ai pas tout lu mais la lecture ligne par ligne du fichier m'a sauté aux yeux
                              std::string line;
                              while(std::getline(file, line))
                                  code.push_back(line);
                              
                              • Partager sur Facebook
                              • Partager sur Twitter
                                24 juin 2012 à 15:38:53

                                Citation : De passage

                                Je sais que je suis sensé m'abstenir, mais j'aime enfreindre les interdits : goto c'est mal, j'ai pas lu ton lien mais blablabla <insert troll>...
                                Plus sérieusement, pour ce que fait ton goto tu pouvais obtenir le même résultat en écrivant directement directement return dans le switch. On pouvais aussi s'en sortir avec un booléen pour vérifier que le programme continue de tourner.



                                Cette réponse me réconforte. Elle me prouve que effectivement, tu n'as pas lu l'article, et que tu devrais le faire. Peut-être changerais-tu radicalement d'avis sur "goto" (attention, ne surtout pas y voir l'extrême opposé : une utilisation massive de goto). De toute façon, si j'ai utilisé goto, c'est avant tout pour satisfaire mon sens de la provocation (et ça marche). :p


                                Citation : De passage

                                Par conséquent je pense qu'il serait judicieux de mettre init en private et d'y faire appel au début de start.



                                Très juste !


                                Citation : De passage

                                en fait il y a un dernier truc qui me chiffonne. Je n'aime pas l'idée de laisser à l'utilisateur le soin de remplir un tableau de std::string



                                Quel utilisateur ? En quelque sorte, je suis le seul utilisateur de cette classe, donc quelque part, je m'en sers dans la main() comme bon me semble. Ce n'est pas comme si j'avais publié la classe "Befunge" avec une note "servez-vous en dans vos programmes, c'est gratuit". La finalité de mon code, c'est l'exécution, pas la distribution. Mmh et de plus, ça me semble pas si mal de passer le code en paramètre du constructeur. De toute façon si quelqu'un (ton "utilisateur") est capable d'utiliser ma classe et de coder un main(), il sera capable de récupérer correctement le code du fichier. En plus, on n'est même pas sûr qu'il veuille récupérer le code d'un fichier, ma solution me semble donc la plus souple...


                                Citation : Hugooguh

                                Et puis, tout compte fait, je trouve ça pas très bien, la non-protection contre les SEGFAULT



                                J'y avais pensé, puis j'me suis dit boarf, mais non tu as raison c'est pas bien.
                                En fait si je devais chipoter (mais je n'ai pas le cœur à le faire), je te dirais que c'est à celui qui programme en Befunge de s'assurer qu'il n'y a pas de segfault dans son code. Je pourrais même faire preuve de mauvaise fois et dire que c'était pour des raisons d'efficacité, blabla. Mais bon c'est un point que je vais modifier.


                                Citation : Hugooguh

                                Sinon, t'as mis une licence pour ton code ? Je pourrais peut-être l'intégrer comme autre interpréteur pour mon IDE



                                Je me vois mal commencer à faire le chieur pour une solution bâclée en 1h à un exercice de programmation :-°
                                Bien sûr, copie et modifie ce que tu veux.


                                Citation : Hugooguh

                                Il ne manquerait plus que les breakpoints, et un accès aux données, et ça serait super.



                                Je n'ai pas codé cet interpréteur en ayant en tête d'en faire un IDE ou un truc super bien fourni en features, donc c'est normal. En fait, il n'y a là rien de plus qu'un interpréteur de Befunge pur.


                                Citation : Chlab_lak

                                Je n'ai pas tout lu mais la lecture ligne par ligne du fichier m'a sauté aux yeux



                                Si ça compte tellement à tes yeux, alors je vais modifier ça ;)
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  24 juin 2012 à 17:11:38

                                  Citation : shareman

                                  Cette réponse me réconforte. Elle me prouve que effectivement, tu n'as pas lu l'article, et que tu devrais le faire. Peut-être changerais-tu radicalement d'avis sur "goto" (attention, ne surtout pas y voir l'extrême opposé : une utilisation massive de goto). De toute façon, si j'ai utilisé goto, c'est avant tout pour satisfaire mon sens de la provocation (et ça marche). :p

                                  J'ai lu l'article. Je l'avais survolé une première fois, et ma lecture confirme ma première impression : si je ne nie pas que tes arguments sont intéressants, aucun ne s'applique à la situation présente. Se servir de goto si c'est la manière la plus simple et/ou la plus lisible, très bien, faire un traitement d'erreurs en fin de fonction, soit, mais là on a le mot-clé return qui fait (et signifie) exactement ce que tu code avec ton goto, et aucune opération n'est effectuée en sus. Ici le lecteur se retrouve obligé de scroller jusqu'en bas pour découvrir la signification du goto, c'est-à-dire rien.

                                  Citation

                                  Quel utilisateur ? En quelque sorte, je suis le seul utilisateur de cette classe, donc quelque part, je m'en sers dans la main() comme bon me semble. Ce n'est pas comme si j'avais publié la classe "Befunge" avec une note "servez-vous en dans vos programmes, c'est gratuit". La finalité de mon code, c'est l'exécution, pas la distribution. Mmh et de plus, ça me semble pas si mal de passer le code en paramètre du constructeur. De toute façon si quelqu'un (ton "utilisateur") est capable d'utiliser ma classe et de coder un main(), il sera capable de récupérer correctement le code du fichier. En plus, on n'est même pas sûr qu'il veuille récupérer le code d'un fichier, ma solution me semble donc la plus souple...


                                  L'utilisateur, à la rigueur ça peut être toi dans dix ans. Alors ok remplir un tableau de std::string ce n'est pas bien compliqué, et je n'ai aucun doute que tu seras toujours aussi capable de le faire, mais de manière générale, vu que l'on est sur un forum pour débutants et que certains seront peut-être amenés à se servir de ton code comme exemple, autant leur donner de bonnes pratiques. Le but de l'OO c'est de cacher l'aspect données, et à mon sens recevoir std::vector<std::string> en paramètre du constructeur c'est déjà obliger l'utilisateur (oui oui, encore lui) a travailler directement sur des données pour faire marcher ta classe, ce dont on pourrait facilement se passer.
                                  Au niveau de la méthode d'entrée à la rigueur on peut généraliser rapidement en utilisant un template (au moins au niveau du constructeur, quitte à reformer un std::vector<std::string> en interne). Je ne me suis pas replongé dans le code, mais à priori toute classe permettant d'appeler operator[] deux fois consécutives et size() peut faire l'affaire avec un minimum de travail (et éventuellement une classe de politiques).

                                  Pour conclure sur "l'utilisateur" :

                                  Citation : shareman

                                  Ce n'est pas comme si j'avais publié la classe "Befunge" avec une note "servez-vous en dans vos programmes, c'est gratuit"

                                  Citation : shareman

                                  Citation : Hugooguh

                                  Sinon, t'as mis une licence pour ton code ? Je pourrais peut-être l'intégrer comme autre interpréteur pour mon IDE


                                  Je me vois mal commencer à faire le chieur pour une solution bâclée en 1h à un exercice de programmation :-°
                                  Bien sûr, copie et modifie ce que tu veux.

                                  :lol:
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    24 juin 2012 à 18:08:24

                                    Citation : De passage

                                    J'ai lu l'article. Je l'avais survolé une première fois, et ma lecture confirme ma première impression : si je ne nie pas que tes arguments sont intéressants, aucun ne s'applique à la situation présente. Se servir de goto si c'est la manière la plus simple et/ou la plus lisible, très bien, faire un traitement d'erreurs en fin de fonction, soit, mais là on a le mot-clé return qui fait (et signifie) exactement ce que tu code avec ton goto, et aucune opération n'est effectuée en sus. Ici le lecteur se retrouve obligé de scroller jusqu'en bas pour découvrir la signification du goto, c'est-à-dire rien.



                                    En fait, ce ne sont pas mes arguments, c'est un article écrit par rz0.

                                    Mais pour que cette discussion soit tout à fait claire, je précise quand même je suis d'accord avec toi : mon goto est loin d'être nécessaire. Après pour ce qui est de la sémantique, elle est meilleure avec un goto qu'avec un booléen (et sans vouloir en faire une affaire personnelle, je ressens une profonde répulsion pour ces booléens qu'on place un peu partout pour casser des boucles, ou des boucles imbriqués, ou boucles contenant un switch (où un break ne fait plus l'affaire)). Donc si j'ai le choix entre booléen et goto, je prends un goto (si rien de plus "traditionnel" (comme un break) ne fonctionne, évidemment).

                                    Là j'aurais également pu placer directement une instruction return, mais bien parce qu'il n'y a plus de traitement après la boucle de l'interpréteur. En supposant que j'adopte l'idée du return, si ton utilisateur (:p) avait voulu ajouter du code tout à la fin de start(), par exemple pour afficher l'état de la pile après interprétation, il aurait été obligé de trouver et de modifier l'action associée à l'instruction '@', voire d'ajouter un système de booléen très moche (et moins efficace). Ca va à l'encontre du principe d’accessibilité du code, que tu défends pourtant.

                                    Et si on veut chipoter bien à fond, on pourrait dire que "goto end", ce n'est même plus du C++, c'est de l'anglais. Donc niveau clarté c'est imbattable.


                                    Citation : De passage

                                    Pour conclure sur "l'utilisateur" : [...] :lol:



                                    :-°

                                    Bon faut quand même en trouver l'usage, et Hugooguh l'a trouvé. Donc libre à lui, mais disons que mon code n'a pas été écrit pour être distribué tel une bibliothèque. Ca change beaucoup de choses.
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      24 juin 2012 à 21:37:02

                                      goto c'est bien et propre en C. Parce que ça permet d'être SESE et de gérer les ressources pour les chemins non nominaux de façon claire et propre (car factoriser).
                                      En C++ avec le RAII cette utilisation de goto est inutile et c'est clairement une mauvaise pratique.
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        24 juin 2012 à 21:47:49

                                        Citation : Goten_

                                        En C++ avec le RAII cette utilisation de goto est inutile et c'est clairement une mauvaise pratique.



                                        Tu peux me dire ce que mon utilisation a à voir avec le RAII ? J'ai l'impression que tu arrives à un moment donné dans la conversation, t'as vu "goto" et ça y est. Je me suis donné la peine de m'expliquer dans le post juste au-dessus du tiens, donc si tu n'es pas d'accord, merci d'être plus précis et de me répondre en argumentant.
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          25 juin 2012 à 2:18:05

                                          3 utilisations valides de goto:
                                          - machines à état compliquées -> cf les résultats de lex&yacc
                                          - gestion des ressources dans un monde sans exceptions (i.e. : en C)
                                          - faire un break qui sort de plus d'une boucle (/ et autres switchs)

                                          (après il existe des hacks plus forcément utiles: Duff Device, etc))

                                          Comment dire que "goto end" correspond au cas n°2 (de par le nom du tag), et qu'il est caduque en C++ grâce au RAII. D'où la remarque de Goten_.
                                          Maintenant, tu fais un truc bizarre. Tu utilises un truc qui détourne le flot normal (guère SESE), pour faire du SESE.
                                          Je suis d'accord avec toi pour l'utilisation de booléens à la noix pour passer en SESE. Mais va plus loin -> return. Cela aura le mérite d'être bien plus clair que end qui a une autre connotation -> gestion des ressources.
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                          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.
                                            26 juin 2012 à 1:00:19

                                            Ma réponse à l'exercice "Parseur d'expressions mathématiques" lancé par Carma001



                                            Alors, j'espère ne décevoir personne en disant que mon programme ne répond en réalité pas vraiment aux exigences de l'énoncé. Ce dernier propose de parser l'expression d'une fonction, puis de demander la valeur des paramètres avant de calculer le résultat. Ma solution est plus directe : elle permet l'évaluation d'expressions mathématiques arbitrairement complexes. En contre-partie, il est impossible de définir de fonction.

                                            Mon programme est en réalité un interpréteur interactif qui se veut rapide : aucune analyse lexicale n'est faite, tout comme je me passe de la création d'un AST explicite. L'expression est analysée et calculée en une seule passe. Voici une liste de quelques caractéristiques :

                                            - gestion des entiers et des nombres à virgule (avec un '.'), sans faire de différence entre les deux
                                            - gestion des constantes e, pi et du nombre d'or (mais vous pourrez facilement ajouter vos constantes moyennant une (très) petite intervention dans le code -> personne n'est parfait)
                                            - gestion des fonctions cos, sin, tan, acos, asin, atan, cosh, sinh, tanh, exp, ln, log, sqrt, abs, ceil et floor, à écrire telles quelles.
                                            - parfaite gestion des priorités et de l'associativité des opérateurs + - * / ^
                                            - deux types de parenthèses au choix : () et []
                                            - (petite) gestion des erreurs avec indication de leur emplacement "à la caml"
                                            - dernier résultat calculé accessible en utilisant la variable "last"


                                            Le code :

                                            #include <iostream>
                                            #include <string>
                                            #include <cctype>
                                            #include <cmath>
                                            #include <stdexcept>
                                            #include <type_traits> // std::is_same
                                            #include <algorithm> // std::remove_if
                                            #include <functional> // std::equal_to
                                            
                                            #include <boost/mpl/assert.hpp>
                                            #include <boost/unordered_map.hpp>
                                            #include <boost/function.hpp>
                                            
                                            namespace calculator
                                            {
                                                template < typename Iterator >
                                                class Parser
                                                {
                                                  public : // typedef publics...
                                            
                                                    typedef Iterator iterator;
                                                    typedef typename Iterator::value_type value_type;
                                            
                                                  private :
                                            
                                                    double last; // conserve le dernier résultat
                                                    iterator begin, end; // le "range" à parser
                                                    std::size_t pos;
                                            
                                                    boost::unordered_map < std::string, double > const_; // les constantes
                                                    boost::unordered_map < std::string, boost::function < double(double) > > funs; // les fonctions
                                            
                                                    // quelques méthodes bien pratiques :
                                            
                                                    value_type forward()
                                                    {
                                                        value_type ret = *begin++;
                                                        ++pos;
                                                        return ret;
                                                    }
                                            
                                                    bool match(const value_type& c)
                                                    {
                                                        if(begin == end || *begin != c)
                                                            return false;
                                                        forward();
                                                        return true;
                                                    }
                                            
                                                    void eat(const value_type& c)
                                                    {
                                                        if(!match(c))
                                                            throw std::logic_error(std::string("'") + c + "' is expected");
                                                    }
                                            
                                                    int match_digit()
                                                    {
                                                        int n = -1;
                                                        if(begin != end && std::isdigit(*begin))
                                                            n = forward() - '0';
                                                        return n;
                                                    }
                                            
                                                  public : // Constructeur et parsing :
                                            
                                                    Parser()
                                                      : last(0.)
                                                    {
                                                        BOOST_MPL_ASSERT((std::is_same < value_type, char >));
                                            
                                                        const_["pi"] = 3.1415926535;
                                                        const_["e"]  = 2.7182818284;
                                                        const_["or"] = 1.6180339887;
                                            
                                                        typedef double(*fun)(double);
                                            
                                            #define MAP(name, stdfun) funs[#name] = static_cast < fun >(std::stdfun);
                                                        MAP(cos, cos)
                                                        MAP(sin, sin)
                                                        MAP(tan, tan)
                                                        MAP(acos, acos)
                                                        MAP(asin, asin)
                                                        MAP(atan, atan)
                                                        MAP(cosh, cosh)
                                                        MAP(sinh, sinh)
                                                        MAP(tanh, tanh)
                                                        MAP(exp, exp)
                                                        MAP(ln, log)
                                                        MAP(log, log10)
                                                        MAP(sqrt, sqrt)
                                                        MAP(abs, fabs)
                                                        MAP(ceil, ceil)
                                                        MAP(floor, floor)
                                            #undef MAP
                                                    }
                                            
                                                    std::size_t position()
                                                    {
                                                        return pos;
                                                    }
                                            
                                                    double start(iterator begin_, iterator end_)
                                                    {
                                                        begin = begin_, end = end_;
                                                        pos = 0;
                                            
                                                        double n = expr();
                                                        if(begin != end)
                                                            throw std::logic_error("Incomplete parsing");
                                                        return (last = n);
                                                    }
                                            
                                                    // the parse members :
                                            
                                                    double expr() // + et -
                                                    {
                                                        double n = term();
                                            
                                                        while(begin != end)
                                                              if(match('+')) n += term();
                                                         else if(match('-')) n -= term();
                                                         else break;
                                            
                                                        return n;
                                                    }
                                            
                                                    double term() // * et /
                                                    {
                                                        double n = pow_();
                                            
                                                        while(begin != end)
                                                              if(match('*')) n *= pow_();
                                                         else if(match('/')) n /= pow_();
                                                         else break;
                                            
                                                        return n;
                                                    }
                                            
                                                    double pow_() // uniquement ^
                                                    {
                                                        double n = fact();
                                                        if(match('^'))
                                                            n = std::pow(n, pow_());
                                                        return n;
                                                    }
                                            
                                                    double fact() // opérandes les plus prioritaires :
                                                    {
                                                        double n = 0.;
                                            
                                                        if(begin == end)
                                                            throw std::logic_error("Missing factor");
                                            
                                                        // plusieurs styles de parenthèses :
                                                        if(match('('))
                                                            n = expr(), eat(')');
                                                        else if(match('['))
                                                            n = expr(), eat(']');
                                            
                                                        // signe d'une expression :
                                                        else if(match('-'))
                                                            n = -fact();
                                                        else if(match('+'))
                                                            n = fact();
                                            
                                                        // constantes numériques, mathématiques ou appels de fonction :
                                                        else if(std::isdigit(*begin))
                                                            n = number();
                                            
                                                        else if(std::isalpha(*begin))
                                                        {
                                                            std::string id = ident();
                                            
                                                            if(id == "last") // demande le dernier résultat calculé
                                                                n = last;
                                            
                                                            else if(match('(')) // c'est un appel de fonction
                                                            {
                                                                if(funs.find(id) == funs.end())
                                                                    pos -= 2, // on ne tient plus compte de '('
                                                                    throw std::logic_error(id + " is not a function");
                                            
                                                                n = funs[id](expr()), eat(')');
                                                            }
                                                            else // c'est une constante
                                                            {
                                                                if(const_.find(id) == const_.end())
                                                                    --pos,
                                                                    throw std::logic_error(id + " is not a constant");
                                                                n = const_[id];
                                                            }
                                                        }
                                            
                                                        else throw std::logic_error("Unexpected input");
                                            
                                                        return n;
                                                    }
                                            
                                                    double number() // le role de cette méthode est de parser un nombre entier ou à virgule flottante
                                                    {
                                                        int t;
                                                        double n = 0., d = 10.;
                                            
                                                        while((t = match_digit()) != -1)
                                                            n = 10. * n + static_cast < double >(t);
                                            
                                                        if(match('.'))
                                                            while((t = match_digit()) != -1)
                                                                n += static_cast < double >(t) / d, d *= 10.;
                                                        return n;
                                                    }
                                            
                                                    std::string ident() // récupère un identifiant
                                                    {
                                                        iterator it = begin;
                                                        while(begin != end && std::isalnum(*begin))
                                                            forward();
                                            
                                                        return std::string(it, begin);
                                                    }
                                                };
                                            }
                                            
                                            int main()
                                            {
                                                std::string expr;
                                                calculator::Parser < std::string::const_iterator > parser;
                                            
                                                while(true)
                                                {
                                                    std::cout << "# ";
                                                    std::getline(std::cin, expr);
                                            
                                                    if(expr == "exit")
                                                        break;
                                            
                                                    expr.erase(std::remove_if(expr.begin(), expr.end(), std::bind1st(std::equal_to < char >(), ' ')), expr.end());
                                                    try
                                                    {
                                                        std::cout << "> ";
                                                        std::cout << parser.start(expr.begin(), expr.end()) << std::endl;
                                                    }
                                                    catch(const std::exception& err)
                                                    {
                                                        std::cout << "Error : " << err.what() << std::endl;
                                                        std::cout << "> " << expr << std::endl;
                                                        std::cout << std::string(parser.position() + 2, ' ') << '^' << std::endl;
                                                    }
                                                }
                                            
                                                return 0;
                                            }
                                            



                                            Petite démo (les '#' sont les entrées, les '>' correspondent aux sorties) :

                                            # (4+2.3) * 10
                                            > 63
                                            # ((4 * 5]) - 3
                                            > Error : ')' is expected
                                            > ((4*5])-3
                                                   ^
                                            # sqrt(2)
                                            > 1.41421
                                            # e^3
                                            > 20.0855
                                            # exp(3)
                                            > 20.0855
                                            # 2^5
                                            > 32
                                            # 2*2*-2
                                            > -8
                                            # ---5
                                            > -5
                                            # sin(1
                                            > Error : ')' is expected
                                            > sin(1
                                                   ^
                                            # abs(sin(-1)-3)
                                            > 3.84147
                                            # -2 * bla(4*2) / cos(2.3)
                                            > Error : bla is not a function
                                            > -2*bla(4*2)/cos(2.3)
                                                   ^
                                            # (1+sqrt(5)) / 2
                                            > 1.61803
                                            # or
                                            > 1.61803
                                            # ln(e^3)
                                            > 3
                                            # floor(2.3+4)*
                                            > Error : Missing factor
                                            > floor(2.3+4)*
                                                           ^
                                            # exit


                                            Bien entendu, toute critique est la bienvenue (pas de goto cette fois :p).

                                            shareman
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              26 juin 2012 à 7:34:43

                                              je dit ouah, le seul défaut je trouve c'est que quand tu met la fonction blabla, le ^ montre l'opération d'après, je sais pas si c'est exprès mais je trouve qu'il devrait montrer le mileu de la fonction qui plante ou le début ;)
                                              Sinon c'est assez magnifique ...
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                26 juin 2012 à 9:51:30

                                                Beau boulot. Et en effet, jamais de comparaison sur un flottant.
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                Anonyme
                                                  26 juin 2012 à 11:42:24

                                                  Moi, plutôt que de recréer plein de fonctions dans un namespace, j'aurais plutôt utilisé using std::blabla;, mais bon... À part ça, c'est du bon boulot.

                                                  Par contre, (t = match_digit()) != -1., ça marchera parce que tu ne fais pas de calculs dessus et que c'est exactement la valeur que tu as mise comme erreur dans la fonction (je ne vois pas pourquoi ça serait un double différent)... Mais tu aurais pu mettre une petite exception, pour faire propre... Ou alors un int. Là tu sera tranquille, et ça ne gène pas.
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    26 juin 2012 à 14:08:17

                                                    J'ai édité le code, en tenant compte de vos remarques. :)

                                                    Citation : PITETRE

                                                    je dit ouah, le seul défaut je trouve c'est que quand tu met la fonction blabla, le ^ montre l'opération d'après, je sais pas si c'est exprès mais je trouve qu'il devrait montrer le mileu de la fonction qui plante ou le début ;)



                                                    En fait cela vient du fait que l'argument est analysé avant de vérifier que la fonction en est bien une (ce qui avance mon compteur interne). J'ai édité, désormais la vérification est faite dès qu'on lit '(' (ce qui différencie une fonction d'une constante), et s'il y a erreur, on le recule de deux cases, ce qui le positionnera sur la dernière lettre de l'identificateur. Du coup, j'ai aussi édité la démo pour tenir compte de ce changement.


                                                    Citation : Hugooguh

                                                    Moi, plutôt que de recréer plein de fonctions dans un namespace, j'aurais plutôt utilisé using std::blabla;



                                                    Oui, c'était assez moche ça, j'ai édité. J'utilise désormais directement les fonctions de cmath, moyennant un petit cast pour résoudre l’ambiguïté (parce que std::cos et toussah, sont surchargées).


                                                    Citation : Hugooguh

                                                    Par contre, (t = match_digit()) != -1., ça marchera parce que tu ne fais pas de calculs dessus et que c'est exactement la valeur que tu as mise comme erreur dans la fonction (je ne vois pas pourquoi ça serait un double différent)... Mais tu aurais pu mettre une petite exception, pour faire propre... Ou alors un int. Là tu sera tranquille, et ça ne gène pas.



                                                    Oui, j'ai finalement mis un int, quitte à caster t pour rester propre, merci ;)

                                                    EDIT : J'ai également intégré la gestion d'une variable qui stocke systématiquement le dernier résultat calculé. Vous pouvez donc le réutiliser dans l'expression suivante, simplement en écrivant "last". Par exemple :

                                                    # sqrt(2.)
                                                    > 1.41421
                                                    # last / 2
                                                    > 0.707107
                                                    # -last
                                                    > -0.707107


                                                    Bon, du coup ce n'est plus le constructeur qui prend le "range" à parser en paramètre, mais bien start(), afin de pouvoir l'appeler autant de fois que l'on veut, sans avoir à détruire le parseur.
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      2 juillet 2012 à 9:19:19

                                                      bonjours à vous, je ne sais pas si je peut le demmander ici, voici mon problème :
                                                      Je suis entrain de faire l'exercice du puissance 4 au quel j'aimerais mettre un A.I j'ai donc essayé de faire une vérification la plus efficace possible, c'est à dire que la victoire est testé après chaque coups en utilisant le fait que s'il y a victoire, cela contient forcément le dernier coups joué. Mais j'ai un petit pb et mon algo ne fonctionne pas dans tout les cas ...
                                                      Alors qu'en pensez vous, devrais-je poster ici les détails, ou bien faire un nouveau sujet ?
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                      Anonyme
                                                        2 juillet 2012 à 11:16:00

                                                        Crée un nouveau sujet, plutôt...
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          2 juillet 2012 à 14:49:15

                                                          Et poste ici un lien vers ce sujet ;)
                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            2 juillet 2012 à 17:52:54

                                                            ben j'ai pas encore eu le temps de le faire (répondre rapidement ici ca va mais faire un topic est un peu plus long, mais je le ferait ^^ )

                                                            EDIT : voici le liens promis
                                                            • Partager sur Facebook
                                                            • Partager sur Twitter

                                                            [Exercices] Venez vous entraîner !

                                                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                            • Editeur
                                                            • Markdown