01net    Web


Actuellement en ligne : 978 Utilisateurs dont 58 dans Programmation et développement >S'inscrire      >S'identifier      >Recherche      >Aide  
modéré par A.Ouloube, Beno@  
01net > Forum de 01net > Programmation et développement > C/C++
> besoin d'aide pour projet en c
Auteur
Message
 
<     1       >
yozman
  
   
      ?   @     Posté le 04/01/2006 17:01:37  
Voter pour ce message
bonjour, je suis étudiant et j'ai un projet a faire, il faut que je programme une calculatrice:
en gros je doit faire une fonction qui évalue des expression arithmétique ac les opérateurs +-*/ et les parentheses. en rentrant une expression de ce type :(2+3)*2 ma fonction doit me donner le résultat. et je dois utiliser une pile pour les opérateurs et les opérandes.comme je suis un débutant en programmation, je suis un peu perdu...
Si on pouvait me donner des indication pour la demarche a suivre, ca serait cool, parce que je sais trop par ou commencer la. merci
Beno@
  
  :-)
      ?   @     Posté le 04/01/2006 18:24:58  
Voter pour ce message
quel language?

une calculette ca ne va pas etre enormement difficile pour les opperation de base vu que les opérateurs sont deja existants...
yozman
  
   
      ?   @     Posté le 04/01/2006 20:23:40  
Voter pour ce message
le programme est a faire en C...
Sylvain31
  
   
      ?   @     Posté le 05/01/2006 09:56:02  
Voter pour ce message
Puisque je suppose que tu n'as pas le droit d'utiliser des outils comme lex et yacc (lexeurs/parseurs en C), commence simplement par faire un petit bout de code qui lit ta chaine de caractères et la transforme en tokens. Si tu as du mal, commence par faire une machine à état qui fait ça.

Une fois que tu as fait ça, tu peux commencer la programmation de ta calculette.
L'idée est assez simple: c'est d'utiliser ta pile pour stocker les opérations de faible priorité, et pour regroupper tes expressions prenthésées.

Tu places chaque token dans ta pile au fur et à mesure:
Lorsque tu rencontres un opérateur prioritaire (* ou /), tu regardes si le token suivant est un nombre. Si oui, tu évalues l'opération et tu remplace les éléments de l'opération par leur résultat dans ta pile.
Lorsque tu rencontres une parenthèse fermante, tu remontes ta pile en évaluant les opérations jusqu'à ce que tu rencontres une parenthèse ouvrante. Là, tu regardes si l'opérateur restant dans ta pile (s'il y en a un) est un opérateur prioritaire. Si oui, tu évalues l'opération. Tu remplaces tout ce que tu as utilisé dans ta pile par le résultat.

A chaque instant, tu n'as dans ta pile que:
- des opérandes,
- des parenthèses ouvrantes,
- des opérateurs + et -,
- éventuellement des opérateurs * ou / avant une parenthèse ouvrante (et uniquement avant une parenthèse ouvrante).

J'espère que ça répond à ta question et que je ne me suis pas planté ;-). D'ailleurs c'est plus une réponse algorithmique qu'une réponse en C...
yozman
  
   
      ?   @     Posté le 05/01/2006 12:32:55  
Voter pour ce message
wouhhh.... ca m'a l'air bien compliqué tout ca, je commence a me demander si je vais arriver a le faire ce projet.... :/
ouais, je voudrais savoir c quoi en fait un token?
- cette fonction qui transforme ma chaine de caractère en token, c'est pour différencier les opérateurs et les opérandes?
Sylvain31
  
   
      ?   @     Posté le 05/01/2006 12:51:03  
Voter pour ce message
Un token, c'est juste un truc abstrait qui représente un élément de ton expression. Dans notre cas, les opérateurs, les parenthèses et les opérandes sont des tokens.
A ne pas confondre avec des caractères: les opérandes sont constituées de plusieurs caractères. C'est pour ça que j'ai parlé de token. Ne te laisse pas impressionner par ce genre de termes, c'est très simple en fait.
Désolé de t'avoir effrayé, c'était pas le but.

Pour répondre à ta question, cette fonction transforme la chaine de caractères:

(23 + 17) * 456

en
Token ( de type parenthèse ouvrante,
Token 43 de type "nombre",
Token + de type opérateur +,
Token 17 de type nombre,
Token ) de type parenthèse fermante,
Token * de type opérateur *,
Token 456 de type "nombre".

En C un token peut être représenté par une structure du genre:

#define OPEN_PAR 1
#define CLOSE_PAR 2
#define NUMBER 3
#define OPERATOR_PLUS 4
#define OPERATOR_MINUS 5
#define OPERATOR_MUL 6
#define OPERATOR_DIV 7

struct Token {
int type; // Type de token
char* chaine; // Le char* correspondant
int data; // Les données éventuelles (pour un entier, ce sera la valeur du nombre... à remplacer par double si tu veux des nombres à virgule...)
};

PS: c'est un peu prise de tête, mais c'est pas compliqué du tout. Ne t'inquiètes pas, tu y arriveras.
-->Message édité par Sylvain31 le 05/01/2006 12:52:51<--
yozman
  
   
      ?   @     Posté le 05/01/2006 18:01:35  
Voter pour ce message
oui, merci pour l'explication pour les tokens.
Donc si j'ai bien compris, apres avoir définis une structure pour les tokens, je doit écrire une fonction pour convertir ma chaine de caractère en token. (c'est bien une fonction que je dois ecrire?)
mais la, je vois vraiment pas comment faire pour la coder...
je sais pas si je l'ai dit, mais je débute en programmation.

PS:petite précision, je dois ecrier un programme qui évalue les expression aritmétique sur un seul chiffre décimal, ce qui , j pense facilite un peu les choses.
Sylvain31
  
   
      ?   @     Posté le 05/01/2006 18:16:57  
Voter pour ce message
Effectivement, ça facilite: plus besoin de tokens... juste des caractères.

Donc tu n'as plus besoin de fonction lexeur.

Analyser une chaine de caractères est un truc très simple. Une chaine de caractères c'est une suite de char, dont le premier est à une adresse mémoire spécifiée par un char*, et dont le dernier a la valeur 0.
Donc, pour analyser chaque caractère de ta chaine, tu fais un truc du genre:

char* machaine = "(2+3*4)*4";
char* caractère;

for (caractère = machaine ; caractère++ ; *caractère != 0)
{
switch (*caractère) {
case '(': // Traitement pour parenthèses ouvrantes...
break;
case '+': // Traitement pour +...
...
default: // Traitement pour le reste...
break;
}
}

Pour les détails, faut relire l'algorithme du dessus...
Bon courage.
yozman
  
   
      ?   @     Posté le 05/01/2006 18:24:33  
Voter pour ce message
ah oui d'accord. Je vais essayer de voir tout ca et essayer d'avancer.
Ben je te remercie beaucoup pour les aides que tu m'a deja fournit.
Sylvain31
  
   
      ?   @     Posté le 05/01/2006 18:33:11  
Voter pour ce message
N'hésites pas si t'as besoin...
yozman
  
   
      ?   @     Posté le 06/01/2006 17:28:50  
Voter pour ce message
donc si j'ai bien compris,
je parcours ma chaine caractère:
-si le caractère est un chiffre, un +, un -, ( et *ou/ devant (, alors j'empile.
-si j'ai *ou/ je regarde le caractere suivant, si c'est un chiffre, j'effectue l'opération.
-si j'ai une ) j'évalue en remontant jusqu'a la parenthèse ouvrante.
Mais pour évaluer en remontant, je dois faire comment? utiliser de la récursivité? évaluer en remontant et en faisant l'opération a chaque fois que je rencontre un caractère?
Sylvain31
  
   
      ?   @     Posté le 06/01/2006 17:55:32  
Voter pour ce message
Il faut que tu utilises une pile. (comme décrit dans ton énnoncé).

Par exemple: (2 + 3*2) * 4

PUSH signifie qu'on pousse une valeur dans la pile. POP signifie qu'on retire une valeur dans la pile.


On trouve '('. PUSH '('.
On trouve '2'. PUSH '2'.
On trouve '+'. PUSH '+'.
On trouve '3'. PUSH '3'.
On trouve '*'. On regarde après: on trouve 2. Il faut évaluer.
POP -> on trouve 3
On évalue 3*2 = 6 -> PUSH 6
On trouve ')': remonter jusqu'à '('.
POP -> on trouve 6
POP -> On trouve +
POP -> On trouve 2
On évalue 2+6 = 8
POP -> On trouve '('. Evaluation de la parenthèse terminée. On pousse son résultat:
PUSH 8
Puis on continue la lecture de la chaine.

On trouve '*': le caractère d'après est 4: on évalue:
POP -> On trouve 8
On évalue 4*8 = 32.

Aucun caractère restant: on vérifie que la pile est bien vide (sinon syntax error).

(2 + 3*2) * 4 = 32

C'est tout simple.
yozman
  
   
      ?   @     Posté le 06/01/2006 18:33:08  
Voter pour ce message
oui, d'accord.
L'évaluation je l'integre donc a ma fonction d'analyse de la chaine?
La fonction qui analyse ma chaine de caractère c le main() du programme?

En admettant que j'ai défini une fonction PUSH et POP:

je reprend l'exemple de la multiplication suivant d'un caractere qui est un nombre:
en code ca devrait donner quelque chose qui ressemble a ca?

case '*':
c=caractere++; //ca je sais pas si le compilateur l'accepte...
if (c==NOMBRE)
PUSH(POP() * c);
else if ...

PS:comme je débute, j'écrit peut etre des horreurs, je m'en excuse a l'avance.
Sylvain31
  
   
      ?   @     Posté le 07/01/2006 11:35:53  
Voter pour ce message
C'est un peu plus compliqué que ça. Déjà, organise ton code avec des fonctions (faut prendre de bonnes habitudes et faut structurer ton code, même en C).

Tu vas devoir te faire une fonction du genre:

bool isNumber(char c);
int toNumber(char c);
void push(int c);
int pop();

#DEFINE PLUSOPERATOR -1
#DEFINE MINUSOPERATOR -2
#DEFINE MULOPERATOR -3
#DEFINE DIVOPERATOR -4
#DEFINE OPENPAR -5
#DEFINE CLOSEPAR -6
#DEFINE NOELEMENTINSTACK -7

Ensuite, tu vas avoir plusieurs fonctions du genre:
parse() -> analyse la chaine de caractères.
int evalParenthesis() -> Evalue tout ce qu'il y a dans la pile jusqu'à la parenthèse ouvrante. Si aucune prenthèse ouvrante -> erreur. Retourne ne résultat de l'évaluation

Au niveau variables, tu as:
char* expression; // L'expression d'origine.
char* current; // Le pointeur vers le caractère en cours d'analyse.
int currentValue; // Une valeur en mémoire.

Ta fonction parse:

PUSH(OPENPAR); // On commence par ajouter une parenthèse ouvrante... Permet de regroupper toute l'expression à l'intérieur de parenthèses.
for (current = expression; *current != 0; current++) { // On parcours la chaine
char c = *current;
if (isNumber(c)) { // Si c'est un simple nombre, on l'empile
PUSH(toNumber(c));
}
else {
switch(c) { // Si c'est un opérateur simple non prioritaire, on l'empile
case '(':
PUSH(OPENPAR); break;
case '+':
PUSH(PLUSOPERATOR); break;
case '-':
PUSH(MINUSOPERATOR); break;
case '*': // S'il est prioritaire, on tente d'évaluer
if (isNumber(current[1])) {
int val1 = POP();
int val2;
if (val1 < 0) // opérateur ?
erreur();
current++;
val2 = toNumber(*current);
val1 = val1 * val2;
PUSH(val1);
}
else { // Sinon, on l'empile
if (current[1] != '(')
erreur();
PUSH(MULOPERATOR);
}
break;
case '/':
if (isNumber(current[1])) {
int val1 = POP();
int val2;
if (val1 < 0) // opérateur ?
erreur();
current++;
val2 = toNumber(*current);
val1 = val1 / val2;
PUSH(val1);
}
else {
if (current[1] != '(')
erreur();
PUSH(DIVOPERATOR);
}
break;
case ')': { // Si c'est une parenthèse fermante, on évalue
int val1 = evalParenthesis();
int operator = POP();
// Puis on vérifie s'il y a un opérateur prioritaire avant
if (operator == NOELEMENTINSTACK) {
PUSH(val1);
}
else if (operator == MULOPERATOR) { // Si oui, on l'évalue
int val2 = POP();
if (val2 < 0)
error();
val1 = val2 * val1;
PUSH(val1);
}
else if (operator == DIVOPERATOR) {
int val2 = POP();
if (val2 < 0)
error();
val1 = val2 / val1;
PUSH(val1);
}
else { // sinon, on remet tout en place dans la pile
PUSH(operator);
PUSH(val1);
}
}
break;
default: erreur(); break;
}
}
}
// A la fin, on évalue tout jusqu'à la parenthèse ouvrante qu'on a ajouté.
// nb: tester aussi s'il reste quelque chose dans la pile à la fin. Si oui, erreur...
return evalParenthesis();

La fonction evalParenthesis évalue les opérateurs présents dans la pile. Je rappelle que les seuls opérateurs présents dans la pile, entre deux parenthèses ne peuvent être que des + et des -, (les * et / ont déja été évalués, ou alors ils sont placés juste avant la parenthèse ouvrante). L'algo est alors ssez simple. L'idée, c'est de partir de 0, et d'ajouter ou soustraire à ce nombre l'ensemble des nombres présents dans la pile en fonction de l'opérateur placé devant.

Ta fonction evalParenthesis:

int currentvalue = 0;
int currentoperator = PLUSOPERATOR;
int stackValue;
int nbElements; // Compteur d'éléments entre les parenthèses. Erreur si aucun.

while ((stackValue = POP()) != NOELEMENTINSTACK) { // On remonte la pile
int val = 0;
if (stackValue = OPENPAR) { // Si parenthèse ouvrante, on a fini de remonter
break;
}
if (stackValue < 0) // Si c'est un opérateur, erreur. Ce doit être un nombre
erreur();
nbElements++;
val = stackValue;
currentoperator = stackValue;
if (currentoperator > 0) // Si c'est un nombre, erreur. Ce doit être un opéreur
erreur();
if (currentoperator == OPENPAR) { // Si parenthèse ouvrante, on a fini.
currentValue += val; // On ajoute le dernier nombre.
break;
}
switch(currentoperator) { // Evaluation: il ne peut y avoir que des opérateurs non prioritaires (+ et -)
case PLUSOPERATOR:
currentValue += val;
break;
case MINUSOPERATOR:
currentValue -= val;
break;
default:
erreur();
break;
}
}
if (stackValue != OPENPAR)
erreur();

return currentvalue;

Ca devrait aller mieux maintenant non ?
yozman
  
   
      ?   @     Posté le 07/01/2006 12:22:57  
Voter pour ce message
ah ok, ben je vais regarder. en fait moi j'avais fait, depuis hier, une fonction push, une fonction pop, et le reste l'analyse de la chaine, ds le main...mais je m'en sortai pas. je vasi revoir tout ca. merci.
yozman
  
   
      ?   @     Posté le 08/01/2006 14:51:23  
Voter pour ce message
ouais,ca y est, j'arrive a compiler mon programme! mais à l'éxecution, j'ai une erreur de segmentation... de quoi ca peut venir? les pointeurs?
Sylvain31
  
   
      ?   @     Posté le 08/01/2006 15:16:57  
Voter pour ce message
Très probablement.

Dans ce genre de situations, soit tu as un debugger et tu peux excécuter ton code en pas à pas pour savoir ce qui se passe, soit tu mets des printf un peu partout pour voir ou ça pète.
yozman
  
   
      ?   @     Posté le 08/01/2006 15:39:50  
Voter pour ce message
c'est une erreur qui vient du main? ou bien elle peut etre dans n'importe laquelle des mes fonctions..
Sylvain31
  
   
      ?   @     Posté le 08/01/2006 17:08:55  
Voter pour ce message
Met des printf un peu partout, et dès que tu as ton erreur, tu verras sur lequel de tes printf le programme c'est arrêté.
yozman
  
   
      ?   @     Posté le 08/01/2006 17:59:50  
Voter pour ce message
ouais, j'ai vu ou ca s'arrete, grace au print f, c'est dans ma fonction parse qu'il y a un problème. je met un bout de mon code, c le début de ma fonction parse, si jamais tu vois ce que ca peut etre...


parse()
{
PUSH(OPENPAR); // On commence par ajouter une parenthèse ouvrante... Permet de regroupper toute l'expression à l'intérieur de parenthèses.
-----------l'erreur se trouve entre cette ligne-----------------
for (current = expression; *current != 0; current++) {
char c = *current;
-----------et celle-ci-----------------------------------------
if (isdigit(c)) { // Si c'est un simple nombre, on l'empile
PUSH((int)(c));
............
.....
..
.

yozman
  
   
      ?   @     Posté le 08/01/2006 18:43:11  
Voter pour ce message
je me posai une question la, il me faut une fonction malloc pour affecter de la mémoire dynamique pour l'expression de ma chaine de caractère?
Sylvain31
  
   
      ?   @     Posté le 09/01/2006 10:17:52  
Voter pour ce message
Il faut vérifier le contenu de la variable expression. Cette vartiable est-elle correctement initialisée ? (faire un printf de cette variable avant d'entrer dans la boucle).
Faire aussi un printf("%s\n", current) à chaque boucle.
Vérifier que les PUSH et POP ne font pas de débordement de buffer.
Le code a l'air correct, donc le pétage vient d'ailleurs. (même s'il se produit ici). Vérifier qu'il n'y a pas un current++ ailleurs...

Bref, faut fouiller...
yozman
  
   
      ?   @     Posté le 10/01/2006 10:56:18  
Voter pour ce message
ouais, je n'ai pas réussi à trouver l'erreur a temps, c'était pour hier.
Je pense que je m'y remettrai le mois prochain, les exams approchent...
Ent tout cas je te remercie beaucoup pour toute l'aide que tu m'as donné.
Sylvain31
  
   
      ?   @     Posté le 10/01/2006 11:34:36  
Voter pour ce message
Pas de pb ;)
<     1       >

01net > Forum de 01net > Programmation et développement > C/C++
> besoin d'aide pour projet en c

Aller à :

Page générée en : 0.586s - X2board 2.2

Nous contacter | Charte de confiance | Voir notice légale

Tous droits réservés © 1999 - 2008 Groupe Tests - 01net.


Sites du réseau 01net Network : 01net - 01men - Rmc.fr - Bfmtv.fr - Radiobfm.com - TousLesPodcasts - Micro Achat

Création de site web
Créez vous même un site web de qualité professionnelle et publiez-le sur Internet
Football
Bilan du dernier marché des transferts. Cristiano Ronaldo et Robinho ont fait les gros titres.