PDA

View Full Version : [C++]Expr Algebriche


Luc@s
31-01-2004, 13:03
Sto provando a fare un parser di expr matematiche.
Ho creato la mia bella classina ma ho un problema

#include <list>
#include <string>

// The expression operator enum
typedef enum
{
PLUS = 0,
MINUS,
DIV,
MOD
} OpType;

// The Expression
struct ExprOp
{
OpType op;
std::string op1;
std::string op2;
ExprOp(const std::string &Op1, const std::string &Op2, OpType Op)
{
op1 = Op1;
op2 = Op2;
op = Op;
}
};

typedef std::list<ExprOp *> Expression;
typedef std::list<ExprOp *>::iterator Expression_it;

class CExprParse
{
private:
std::string ris;
Expression expr;
bool ParseEspr(const std::string &value);
public:
CExprParse();
~CExprParse();
CExprParse(CExprParse &p);
bool Exec();
inline const std::string Get();
bool addExpr(const std::string &value);
};

CExprParse::CExprParse()
{
}

CExprParse::CExprParse(CExprParse &p)
{
// no autoassignment
if(&p != this)
{
// TODO
}
}

CExprParse::~CExprParse()
{
}

bool CExprParse::ParseEspr(const std::string &value)
{
unsigned int plus_loc = value.find( "+", 0 );
unsigned int minus_loc = value.find( "-", 0 );
unsigned int div_loc = value.find( "/", 0 );
if(plus_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ),PLUS));
return true;
}
else if(minus_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ), MINUS));
return true;
}
else if(div_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ), DIV));
return true;
}else
{
return false;
}
}

bool CExprParse::addExpr(const std::string &value)
{
return (ParseEspr(value));
}

bool CExprParse::Exec()
{
std::string tmp("");
ExprOp * ex;
// IF EMPTY EXIT
if(expr.empty())
return false;
while(!expr.empty())
{
// RETURN THE PART OF EXRESSION TO EXECUTE
ex = expr.front();
//////////////////////////////////////////
///////// CHECK THE LITTERAL /////////////
//std::string f_l = ;
//std::string s_l = ;
/*
if(f_l == s_l) // ax + bx
{
switch(ex.op)
{
case PLUS:
break;
case MINUS:
break;
case DIV:
break;
}
}else // ax + by
{
switch(ex.op)
{
case PLUS:
break;
case MINUS:
break;
case DIV:
break;
}
}
*/
//////////////////////////////////////////
//////////CHECK THE NUMBERICAL ///////////
//int fist = static_cast< int >();
//int second = static_cast< int >();
//////////////////////////////////////////
////////// EXEC THE OPERARATION //////////

// REMOVE THE ELEMENT
expr.pop_front();
}
return true;
}

const std::string CExprParse::Get()
{
if(Exec())
return ris;
else
new std::string("Can't execute the expression\n");
}


Come proseguo????
La mia implementazione del metodo Exec va bene????

Tnk 10000000000000

/\/\@®¢Ø
31-01-2004, 14:31
Non del tutto, non tanto per alcuni errori che ho notato qua e la' (quando prendi le sottostringhe ad esempio l'operazione finisce da une delle due parti), ma per l'approccio.
Per prima cosa ti interessa valutare singole operazioni (x+y o a/b ad esempio) o espressioni complete come a+b*c ? Mi sembra che la tua implementazione funzioni solo nel primo caso.
Secondo me una cosa che potresti fare (ed e' quella che si fa di solito) e' quella di distinguere per prima cosa i numeri dalle operazioni, non andando a caccia delle operazioni, ma identificando gli elementi della espressione "da destra a sinistra", e creare degli oggetti (tokens) che li rappresentino. Da li' in poi procedere infine all'analisi vera e propria.
Ad esempio potresti fare un oggetto del genere

enum token_type{ NUMBER , SUM , SUB , MUL , DIV };

struct Token
{
Token(token_type t):type(t){}
Token(int i):value(i){}
token_type type;
int value; // solo per il NUMBER
};


e scriverti una funzione del tipo

template<class It>
vector<Token> lex( It begin , It end )
{
vector<Token> result;
while( begin != end )
{
"controlla cosa c'e' all'inizio dell'input"
switch( "tipo" )
{
case "numero":
result.push_back( Token(numero) );
break;
case '+':
result.push_back( Token(SUM) );
...
}
"Togli il valore appena trovato dall'input aggiornando 'begin'"
}
return result;
}

Il trucco e' non andare in cerca di '+' ma controllare cosa c'e' all'inizio (c'e' un '+' ? e' una somma, c'e' una cifra ? E' l'inizio di un numero ... ), aggiungere man mano alla lista e continuare con quel che ti rimane

fatto questo ottieni una lista gia' un po' strutturata dell'input
cosi' che ottieni qualcosa di indipendente dal numero di cifre e/o di spazi che hai, ti aiutera' inoltre ad individuare errori (cosa succede col tuo programa se scrivo "12 32 + 27" ?)

Luc@s
31-01-2004, 14:37
ho pensato di implementare meglio cosi:

#include <list>
#include <string>

// The expression operator enum
typedef enum
{
PLUS = 0,
MINUS,
DIV,
MOD
} OpType;

// The Expression
struct ExprOp
{
OpType op;
std::string op1;
std::string op2;
ExprOp(const std::string &Op1, const std::string &Op2, OpType Op)
{
op1 = Op1;
op2 = Op2;
op = Op;
}
};

typedef std::list<ExprOp *> Expression;
typedef std::list<ExprOp *>::iterator Expression_it;

class CExprParse
{
private:
std::string ris;
Expression expr;
bool ParseEspr(const std::string &value);
public:
CExprParse();
~CExprParse();
CExprParse(CExprParse &p);
bool Exec();
inline const std::string Get();
bool addExpr(const std::string &value);
};

CExprParse::CExprParse()
{
}

CExprParse::CExprParse(CExprParse &p)
{
// no autoassignment
if(&p != this)
{
// TODO
}
}

CExprParse::~CExprParse()
{
}

bool CExprParse::ParseEspr(const std::string &value)
{
unsigned int plus_loc = value.find( "+", 0 );
unsigned int minus_loc = value.find( "-", 0 );
unsigned int div_loc = value.find( "/", 0 );
if(plus_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ),PLUS));
return true;
}
else if(minus_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ), MINUS));
return true;
}
else if(div_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ), DIV));
return true;
}else
{
return false;
}
}

bool CExprParse::addExpr(const std::string &value)
{
return (ParseEspr(value));
}

bool CExprParse::Exec()
{
std::string tmp("");
ExprOp * ex;
// IF EMPTY EXIT
if(expr.empty())
return false;
while(!expr.empty())
{
// RETURN THE PART OF EXRESSION TO EXECUTE
ex = expr.front();
unsigned int x_loc = ex->op1.find( "x", 0 );
unsigned int y_loc = ex->op1.find( "y", 0 );
unsigned int x2_loc = ex->op2.find( "x", 0 );
unsigned int y2_loc = ex->op2.find( "y", 0 );
if(x_loc != std::string::npos && x2_loc != std::string::npos) // ax + bx
{
/*
ax + bx
\
(a + b)x
\
abx
a = op1.substr(0, x_loc);
b = op2.substr(0, x2_loc);
*/
}
else if(x_loc != std::string::npos && y2_loc != std::string::npos) // ax + by
{
/*
ax + by
\
\
ax by
a = op1.substr(0, x_loc);
b = op2.substr(0, y2_loc);
*/
}
else if((x_loc == std::string::npos && y2_loc != std::string::npos)) // ay + by
{
/*
ay + by
\
(a + b)y
\
aby
a = op1.substr(0, y_loc);
b = op2.substr(0, y2_loc);
*/
}
expr.pop_front();
}
return true;
}

const std::string CExprParse::Get()
{
if(Exec())
return ris;
else
new std::string("Can't execute the expression\n");
}


Lo logica dei commmenti in exec è ok???


Tnk

cionci
31-01-2004, 16:16
Ma l'albero di derivazione non te lo sei costruito ?

Luc@s
31-01-2004, 17:13
volevo fare solo una prova x le addizioni con le liste.
Ho sbagliato???

cionci
31-01-2004, 17:46
Se funziona va bene ;)
Comunque studiati come costruire l'albero di derivazione... Fai 10 volte prima ad interpretare l'espressione...

Luc@s
31-01-2004, 17:58
Originariamente inviato da cionci
Se funziona va bene ;)
Comunque studiati come costruire l'albero di derivazione... Fai 10 volte prima ad interpretare l'espressione...

Era un esercizio per usare gli enum e altre cosette appena studiate......anche se nnandra avro imparato qualcosa:)
Cmq se prova ad usare la classe mi termina immediatamente :eek:
P.S: davvero va bene??

cionci
31-01-2004, 18:02
Originariamente inviato da Luc@s
Cmq se prova ad usare la classe mi termina immediatamente :eek:
Debuggalo ;)

Luc@s
31-01-2004, 18:04
Originariamente inviato da cionci
Debuggalo ;)

hai PVT.
Cmq nn sono molto esperto in fatto debug :(:(
Qello del VC++ 6 va bene da usare???

cionci
31-01-2004, 18:16
Originariamente inviato da Luc@s
Qello del VC++ 6 va bene da usare???
IMHO il migliore...

Luc@s
31-01-2004, 18:18
Originariamente inviato da cionci
IMHO il migliore...

Bene cosi imparero qualcosa di + sul debug.
Pvt???

Luc@s
31-01-2004, 18:25
cmq questo è il cod finale:

#include <list>
#include <string>

// The expression operator enum
typedef enum
{
PLUS = 0,
MINUS,
DIV,
MOD
} OpType;

// The Expression
struct ExprOp
{
OpType op;
std::string op1;
std::string op2;
ExprOp(const std::string &Op1, const std::string &Op2, OpType Op)
{
op1 = Op1;
op2 = Op2;
op = Op;
}
};

typedef std::list<ExprOp *> Expression;
typedef std::list<ExprOp *>::iterator Expression_it;

//////////////////////////////////////////////////////////////////////////
// The Expression Class //
//////////////////////////////////////////////////////////////////////////

class CExprParse
{
private:
std::string ris;
Expression expr;
bool ParseEspr(const std::string &value);
public:
CExprParse();
~CExprParse();
bool Exec();
inline const std::string Get();
bool addExpr(const std::string &value);
};

CExprParse::CExprParse()
{
ris = "";
}

CExprParse::~CExprParse()
{
}

bool CExprParse::ParseEspr(const std::string &value)
{
unsigned int plus_loc = value.find( "+", 0 );
unsigned int minus_loc = value.find( "-", 0 );
unsigned int div_loc = value.find( "/", 0 );
if(plus_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ),PLUS));
return true;
}
else if(minus_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ), MINUS));
return true;
}
else if(div_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ), DIV));
return true;
}else
{
return false;
}
}

bool CExprParse::addExpr(const std::string &value)
{
return (ParseEspr(value));
}

bool CExprParse::Exec()
{
// THIS FUNCTION MAKE ONLY PLUS OPERATION
ExprOp * ex;
// IF EMPTY EXIT
if(expr.empty())
return false;
unsigned int
x_loc,
y_loc,
x2_loc,
y2_loc;
while(!expr.empty())
{
// RETURN THE PART OF EXRESSION TO EXECUTE
ex = expr.front();
x_loc = ex->op1.find( "x", 0 );
y_loc = ex->op1.find( "y", 0 );
x2_loc = ex->op2.find( "x", 0 );
y2_loc = ex->op2.find( "y", 0 );
if(x_loc != std::string::npos && x2_loc != std::string::npos) // ax + bx
{
/*
ax + bx
\
(a + b)x
\
abx
a = op1.substr(0, x_loc);
b = op2.substr(0, x2_loc);
*/
std::string
a_s = ex->op1.substr(0, x_loc),
b_s = ex->op2.substr(0, x2_loc);
const char
* a = a_s.c_str(),
* b = b_s.c_str();
int tot = (int)a + (int)b;
ris += tot;
ris += "x";
}
else if(x_loc != std::string::npos && y2_loc != std::string::npos) // ax + by
{
/*
ax + by
\
\
ax by
a = op1.substr(0, x_loc);
b = op2.substr(0, y2_loc);
*/
ris += ex->op1;
ris += ex->op2;
}
else if((y_loc != std::string::npos && y2_loc != std::string::npos)) // ay + by
{
/*
ay + by
\
(a + b)y
\
aby
a = op1.substr(0, y_loc);
b = op2.substr(0, y2_loc);
*/
std::string
a_s = ex->op1.substr(0, y_loc),
b_s = ex->op2.substr(0, y2_loc);
const char * a = a_s.c_str(), * b = b_s.c_str();
int tot = (int)a + (int)b;
ris += tot;
ris += "y";
}
expr.pop_front();
}
return true;
}

const std::string CExprParse::Get()
{
if(Exec())
return ris;
else
new std::string("Can't execute the expression\n");
}



Nn so perche ma se provo addExpr mi da abnormal termination :(

/\/\@®¢Ø
31-01-2004, 18:56
if(plus_loc != std::string::npos)
{
expr.push_back(new ExprOp(value.substr( 0, minus_loc ), value.substr( minus_loc, value.size() ),PLUS));
return true;
}

Sempre stare molto attenti al copia-e-incolla selvaggio ! :D ;)

Luc@s
31-01-2004, 19:02
Originariamente inviato da Luc@s
Nn so perche ma se provo addExpr mi da abnormal termination :(

Luc@s
31-01-2004, 19:04
Ora mia da errore :(

/\/\@®¢Ø
31-01-2004, 19:06
eh, dove ? :p

Luc@s
31-01-2004, 19:11
no anzi.
Risolto.
Tnk

Per i nomi ................. come dovrei chiamarle le var???
Mentre l'ordine è ok, no?

/\/\@®¢Ø
31-01-2004, 19:14
un nome tale che se riguardi il codice dopo una settimana che non lo tocchi capisci ancora cosa fa :D ;)

Luc@s
31-01-2004, 19:22
ora mi da il ris in athac..................why?

cionci
31-01-2004, 19:27
Debug debug debug ;)

F9 per emttere i break point
F5 per far girare il programma in debug (l'esecuzione si ferma sui break point)
F11 e F10 per andare passo-passo
Aggiungi i watch (scrivi il nome della variabile o l'espressione da valutare)...

verloc
01-02-2004, 07:09
Essendo un parser una classe piuttosto complicata,l'hai progettata prima su carta(pseudocodice)?
Fatti una domanda:sono in grado di spiegarne il funzionamento in meno di 30 righe di testo?

In altre parole è chiaro a te stesso come deve funzionare?

Prima progettare poi debuggare altrimenti la confusione viene moltiplicata per 10. :D

Una volta stabilito il funzionamento, il debugging(che Cionci voleva farti intendere non solo come "uso" del debugger)è cosa semplice,visto che statisticamente la maggior parte degli errori sono di tipo veniale.

Luc@s
01-02-2004, 08:26
forse hai ragione.
Cmq uno stralcio di pesudo cod c'è nei commenti.
Cmq ora il mio problema è la visualizzazione :)
Mi prende " 6" al posto che "6" e quindi nn viene.
Mentre 6x + 7y viene :eek:

Cmq tnk ancora per i suggerimenti di questo 3d :D