Partage

[Exercices] Venez vous entraîner !

Ce mois: Parseur de fonctions mathématiques

11 mai 2009 à 11:41:50

Citation : The Joker

Si je comprend bien, on a qu'à récupérer le contenu des deux fichiers ligne par ligne, et après on les compare :

if(ligneFichier1 != ligneFichier2)
{
    cout << "< " << ligneFichier1 << endl;
    cout << "---" << endl;
    cout << "> " << ligneFichier2 << endl;
}


Pour ce qui est de savoir les lignes qui ont été ajoutées/détruites, je pense qu'il faut compter le nombre de lignes dans chacun des deux fichiers : s'ils n'en ont pas le même nombre, c'est qu'il y a des lignes qui ont été ajoutées ou détruites, sinon pas de souci. :D



C'est plus compliqué que ça. Imagine tu as ces deux fichiers, qui ont le même nombre de lignes :
a
b
c
d
e

et
a
c
d
e
f


diff va te donner (il est très malin !) :
2d1
< b
5a5
> f


En clair,
1) Avoir le même nombre de lignes ne prouve rien
2) diff arrive à trouver les déplacements de blocs entiers, une comparaison ligne à ligne n'étant donc pas suffisante.
12 mai 2009 à 12:00:57

je galere un peu sur ce constructeur :
template<size_t N>
    Polynome(const double (&tab)[N])
            :m_termes(N,0.)
    {
        for(size_t i(0); i<N; ++i)
            m_termes[i] = tab[i];
        simplifie();
    }

j'ai regarder le cours sur les templates mais ça ne m'avance pas plus. je comprend bien la fonction mais pas le template<size_t N>.
12 mai 2009 à 13:01:04

Oui. Ca n'est pas encore dans le cours. Les chapitres suivant vont arriver prochainement.

Version simplifiée: On peut également donner un nombre comme argument template. C'est ce que j'utilise ici pour la taille d'un tableau. Cela permet d'éviter d'avoir à donner la taille du tableau en second argument. Plus de détails sur la FAQ de developpe.com par exemple.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
12 mai 2009 à 13:43:55

La boucle pouvait d'ailleurs être évitée ici pour écrire directement: ":m_termes(&tab[0], &tab[N])"
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.
12 mai 2009 à 15:34:18

Citation : lmghs

La boucle pouvait d'ailleurs être évitée ici pour écrire directement: ":m_termes(&tab[0], &tab[N])"



Tiens, c'est pas souvent qu'on l'utilise ce constructeur là... Merci !
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
3 juin 2009 à 14:46:22

Pas de réponse pour Diff ?
J'ai pas mal cherché, mais j'ai abandonné devant la complexité...
Dès qu'on veut faire quelque chose de plus évolué que la comparaison ligne à ligne, et comparer les blocs entre eux pour trouver la plus longue chaine commune, ça devient galère.
3 juin 2009 à 20:50:15

C'est la merveilleuse période des examens... donc la suite arrivera quand j'aurais du temps libre.

Normalement dès lundi.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
10 juin 2009 à 17:47:28

Exercice du mois de juin 2009



Nom : Figlet !
Sujet : Chaînes de caractères, fichiers, affichage


Connaissez-vous Figlet ? C'est certainement le programme le plus utile sous Linux. Il permet d'afficher des messages dans un style "ASCII-art".

figlet "Salut les zeros"
 ____        _       _     _                                   
/ ___|  __ _| |_   _| |_  | | ___  ___   _______ _ __ ___  ___ 
\___ \ / _` | | | | | __| | |/ _ \/ __| |_  / _ \ '__/ _ \/ __|
 ___) | (_| | | |_| | |_  | |  __/\__ \  / /  __/ | | (_) \__ \
|____/ \__,_|_|\__,_|\__| |_|\___||___/ /___\___|_|  \___/|___/


Ce que je vous propose c'est de réaliser ce petit programme. Il devra remplir le cahier des charges suivant:

1) Récupérer les arguments du main pour la phrase à afficher
2) Utiliser le fichier de police (fourni plus bas) pour lire les lettres nécessaires en ASCII-art
3) Afficher le message en ASCII-art.

Si la phrase est trop longue, le programme devra la couper au bon endroit, soit par exemple:

figlet "Salut les zeros dfsdafsdfasdfsd"
 ____        _       _     _                                   
/ ___|  __ _| |_   _| |_  | | ___  ___   _______ _ __ ___  ___ 
\___ \ / _` | | | | | __| | |/ _ \/ __| |_  / _ \ '__/ _ \/ __|
 ___) | (_| | | |_| | |_  | |  __/\__ \  / /  __/ | | (_) \__ \
|____/ \__,_|_|\__,_|\__| |_|\___||___/ /___\___|_|  \___/|___/
                                                               
     _  __         _        __         _  __               _  __         _ 
  __| |/ _|___  __| | __ _ / _|___  __| |/ _| __ _ ___  __| |/ _|___  __| |
 / _` | |_/ __|/ _` |/ _` | |_/ __|/ _` | |_ / _` / __|/ _` | |_/ __|/ _` |
| (_| |  _\__ \ (_| | (_| |  _\__ \ (_| |  _| (_| \__ \ (_| |  _\__ \ (_| |
 \__,_|_| |___/\__,_|\__,_|_| |___/\__,_|_|  \__,_|___/\__,_|_| |___/\__,_|


avec un découpage au niveau des espaces si il y en a et après un certain nombre de caractère si il n'y en a pas.

Vous trouverez les symboles nécessaires à l'affichage dans le fichier police.txt. Il contient une liste de caractères ASCII-art arrangés de la manière suivante:

_ @
 / |@
 | |@
 |_|@
    @@
  ___ @
 |_  )@
  / / @
 /___|@
      @@
  ____@
 |__ /@
  |_ \@
 |___/@
      @@


Chaque ligne de symboles est terminée par un @ et la fin d'un charactère est signifiée par deux @@.

Les charactères sont enregistrés dans le fichier police.txt selon l'ordre ASCII habituel du caractères 32 (l' espace) au caractère 126 (le ~).

Vous remarquerez que lors de l'affichage de "le" par exemple, il n'y a pas de dédoublement des "|" pour les partis communes des deux lettres et c'est la principale difficulté de cet exercice.

figlet "le l e"
 _        _        
| | ___  | |   ___ 
| |/ _ \ | |  / _ \
| |  __/ | | |  __/
|_|\___| |_|  \___|


Ceci est aussi valide si le caractère commun n'est pas le même, mais proche / et | par exemple.

figlet Ab
    _    _     
   / \  | |__  
  / _ \ | '_ \ 
 / ___ \| |_) |
/_/   \_\_.__/


Vous avez jusqu'au 1er juillet pour soumettre vos réponses à Réponse_Exercices.

Bonne chance à tous !
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
10 juin 2009 à 17:55:46

Juste pour signaler une faute dans le titre. :-°
« ficheirs »
10 juin 2009 à 18:17:22

Merci !
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
Anonyme
10 juin 2009 à 21:32:27

Il ne m'a pas l'air simple celui là ! Je vais tout de même me lancer ...
10 juin 2009 à 22:34:49

Faut-il également faire en sorte qu'il y ait superpostion même lorsque les caractères qui sont en contacts ne sont pas identiques?
Par exemple, figlet cache les "|" s'il y a un autre caractère.

Voici ce qu'il affiche pour la chaîne "AB":
figlet AB
    _    ____  
   / \  | __ ) 
  / _ \ |  _ \ 
 / ___ \| |_) |
/_/   \_\____/



Faut-il recopier ce comportement de figlet ou peut-on plutôt afficher:
monfiglet AB
    _     ____ 
   / \   | __ ) 
  / _ \  |  _ \ 
 / ___ \ | |_) |
/_/   \_\|____/

étant donné que \ et | sont des caractères différents et alors n'effectuer cette superposition que si les caractères concordent?

Merci.
11 juin 2009 à 0:10:39

Oui.

Je vais mieux détailler la chose.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
11 juin 2009 à 13:05:59

Toujours là dessus, j'ai l'impression que les obliques l'emportent toujours sur les verticales peu importe leur position (caractère droit ou gauche). : )
Inkamath on GitHub - Interpréteur d'expressions mathématiques. Reprise du développement en cours.
28 juin 2009 à 20:09:59

Bonjour,

comme je ne vois aucun message qui en fait mention, voici une conclusion que j'ai pu obtenir après quelques essais avec figlet lui même et qui pourrait servir a d'autres (bien que ce soit un peu tard, mais je n'ai pris cet exercice qu'hier...)

Il ne s'agit pas d'un simple système de priorité entre les symboles, car certains symboles sont remplacés au moment de la fusions entre deux caractères.

Si je tape ".." dans figlet, celui ci me donne la réponse suivante :
figlet ..
 _ _
(_|_)

On constate que le symbole centrale qui est composé a base de ")" et "(" devient un "|".

Alors que si ça avait été un simple système de priorité des symboles nous aurions probablement obtenu quelque chose qui ressemble à :
figlet ..
 _ _
(_(_)

ou
 _ _
(_)_)

C'est d'ailleurs ce que j'obtenais avec mon exercice jusqu'au moment ou j'ai constaté cette subtilité sur figlet lui même... ;)
Anonyme
5 août 2009 à 22:59:19

Up, j'aurai bien aimé savoir si d'autres exercices allaient suivre :)
15 août 2009 à 20:21:36

Pour la fin des vacances, je vous propose, avec l'accord de Nanoc, un petit exercice qui vous occupera peut-être quelques temps. ;)


Exercice du mois d'août 2009


Nom : Parseur de fonctions mathématiques
Sujet : Chaînes de caractères, algorithme


Après les Bigint, Fractions, et autres Polynômes, je vous propose cette fois de réaliser un parseur de fonctions mathématiques.

Qu'est ce qu'un parseur ? Dans notre cas, le parseur sera l'algorithme qui permettra de transformer une fonction sous forme de chaîne de caractères en une fonction utilisable où il sera possible d'évaluer la valeur de la fonction pour un point donné.

Par exemple, si l'utilisateur entre "2x*(1-3x^2)" et qu'il demande à évaluer la fonction pour x=2, le programme devra lui retourner la valeur -44 (C'est à dire 2*2*(1-3*2^2) ;) )


L'exercice


Niveau 1


Votre programme devra gérer :

  • Les priorités opératoires : les parenthèses, les puissances, les crochets (l'utilisateur pourra employer plusieurs parenthèses, plusieurs crochets, ou les deux s'il le désire), puis *, /, +, - ...
  • Les principales fonctions dont voici la liste : sqrt(x) [racine carrée], pow(x,exp) [alternative à x^exp pour faire une puissance]
  • La constante pi.

A vous de gérer comme bon vous semble les domaines de définition, les erreurs...

PS : Comme me l'a fait remarqué Bad_Wolf, la chaîne a^b^c sera interprétée comme a^(b^c). Je rajoute aussi que la chaîne a^2x*b sera interprétée (a^2)*x*b pour garder la logique de la puissance prioritaire sur la multiplication.


Niveau 2


Si vous avez les connaissances et l'envie, vous pouvez implémenter plus de choses, par exemple :

  • Quelques autres fonctions : rt(x,n) [Racine n-ième], cos(x), sin(x) et tan(x) [les fonctions trigos de base], exp(x) et e^x [fonction exponentielle], ln(x) et log(x,b) [fonctions logarithme népérien et logarithme de base b]
  • D'autres constantes, comme e.

A vous, la aussi, de gérer les cas si on tente de calculer un logarithme négatif ou nul, ou encore la tangente d'un nombre égal à <math>\(\frac{\pi}{2}+k\pi \ , \ k \in \mathbb{Z}\)</math>.

Niveau 3


Si c'est toujours trop facile, pourquoi ne pas donner la possibilité à l'utilisateur d'omettre certains *, c'est à dire de permettre les multiplications implicites entre nombre-variable, nombre-fonction, nombre-parenthèse (ou crochet), variable-parenthèse (ou crochet).


Mot de la fin


Je vous laisse le soin de chercher plus d'informations sur le net, et vous pouvez bien sûr poser des questions aux autres sur ce forum. ;)

Si le cœur vous en dit, vous pouvez créer une classe Fonction et ajouter des fonctionnalités, comme permettre de connaitre le type de fonction (polynomiale, rationnelle, trigonométrique...), de connaitre son domaine de définition, ou encore, pourquoi pas, de renvoyer la dérivée de cette fonction dans un autre objet Fonction . :)

Il n'y a pas de date limite, Nanoc décidera d'arrêter l'exercice quand il le voudra. Cependant, je vous demande exceptionnellement de soumettre vos réponses à moi par MP.

Bonne chance à tous, et si vous avez des questions, n'hésitez pas ! ;)
15 août 2009 à 20:34:41

Bonjour,

Très bonne idée !
Juste quelques petits commentaires histoire que tout le monde se comprenne :
— Dans la liste des priorités, ça devrait commencer par les parenthèses et les crochets, puis les puissances, et après */ et +-. Et en général (par convention), on interprète a^b^c comme a^(b^c).
— Il y a une différence entre les crochets et les parenthèses ? Si oui j'ai pas bien saisi laquelle.
— Pour le hors-domaine (niveau 1), en plus de la division par 0 il ne faut pas oublier les racines carrées de trucs négatifs.

Bon courage à tous !
15 août 2009 à 20:39:07

Quelle méthode ?
- huile de coude ?
- sérieuse/pro ? (lex/yacc, ANTLR, boost.spirit, ...)

PS: evaluer() copie son argument dans son exemple.
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.
15 août 2009 à 20:40:14

Salut,

Oui effectivement, je n'ai pas mis dans l'ordre, mais je vais le faire ;)
Sinon, il n'y a pas de différences entre crochets et parenthèses, on donne juste a l'utilisateur de choisir ce qu'il veut utiliser (vous pouvez même convertir les crochets en parenthèses avant le début du traitement ;) )
Pour la racine carré, oui, je l'ai rajouté juste après avoir posté le message. ;)
15 août 2009 à 20:40:45

lex/yacc c'est peut-être un peu abusé pour une grammaire aussi simple, non ? :-°
15 août 2009 à 20:44:12

La calculatrice est le premier exo d'utilisation de lex/yacc dans les tutoriaux sur le sujet.
(et en toute honnêteté faire ça à l'huile de coude ne me viendrait jamais à l'idée)
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.
15 août 2009 à 20:48:26

Je dis pas le contraire, mais pour avoir codé un parseur dans ce genre là en python (la partie +-*/ et les parenthèses en tout cas) pour me faire la main dans le langage, je peux t'assurer que c'est pas compliqué, et que yacc et lex sont un peu le marteau pilon pour écraser la mouche. :p
Et puis même si évidemment yacc/lex est une combinaison très utile, tu t'es jamais demandé comment on peux faire ça à la main ? Les outils automatiques c'est sympa, mais l'avoir fait au moins une fois c'est pas mal...
Anonyme
15 août 2009 à 20:54:56

Citation : lmghs

(et en toute honnêteté faire ça à l'huile de coude ne me viendrait jamais à l'idée)

Parce que t'as de l'expérience et de bonnes connaissances.
Pour ceux qui ne te connaisse pas, qui ne te croit pas ou autre : j'ai déjà fait un résolveur d'(in)équations de degré quelconque à l'huile de coude. Franchement c'est chiant et débile. ( Bon à l'époque je n'avais même pas entendu parler de grammaire, et encore moins de lex/yacc...
Jetez un oeil si vous me croyez pas : http://gmath.servhome.org/documentation/ . C'est une horreur.
15 août 2009 à 20:57:37

Je sais très bien comment faire ça à la main, c'est pour cela que je préfère utiliser des outils dédiés. Je suis informaticien de formation et de cœur et donc parfaitement fainéant et allergique au NIH.

Si je pose la question, on ne dirait pas mais il y a une logique derrière, c'est pour que cela soit plus explicite dans l'exo. Qu'attendez-vous ?
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.
15 août 2009 à 21:02:10

Ah oui ok, j'avais compris que ton tout premier parseur tu l'avais codé avec lex/yacc... Mais effectivement j'imagine qu'au bout d'un moment coder des parseurs... c'est marrant 5 minutes quoi. ^^
15 août 2009 à 21:49:51

Non, ici le but est de créer son propre parseur, sans se servir d'outils externes. Je pense en effet, comme vous l'avez dit, que ce n'est pas trop "compliqué". ;)
16 août 2009 à 10:15:07

Juste une question, je me demande comment gérer la constante pi. Sinon l'exercice a l'air très bien, je m'y lance.
16 août 2009 à 12:48:42

En fait, si l'utilisateur tape : 2pi*(5+pi), pi devra être remplacé par sa valeur. (Qui est dispo dans <cmath> : cmath.pi ;) )