Domanda Risolvere espressioni matematiche

Stato
Discussione chiusa ad ulteriori risposte.
Ultima modifica:
@St3ve

PHP:
/* questo è per il "risolvere" le singole parentesi... l'ho fatto col più e col meno ma si più facilmente riscrivere 
 * col * e il /
 * per la potenza è un'altra storia... vedi se è adeguato... 
 * non è neanche completo ma è la linea generale, almeno quella a cui pensavo io*/

#include<iostream>
#include<string>
#include<vector>

using namespace std;

int conv_str(string);

int main()
{
/*diamo per scontato che nella parentesi da analizzare le operazioni abbiano tutte la medesima priorità, e 
 * che le priorità siano invece definite tramite le parentesi stesse in modo più rigido.
 * anzi che   2+5*3-3/1   l'utente dovrà    2+(5*3)-(3/1)
 * un piccolo sforzo per l'utente, un grandissimo vantaggio per noi! Nonchè la maggiore facilità di gestione
 * degli errori.
 * sacrificio accettabile... per me almeno! :P */

  string esp, tmp;
  vector<char> op;
  vector<int> vals;
  int ris, i;
  
  cout<<"espressione = "; cin>>esp;
  tmp=esp;  //lavoro prima sui numeri con tmp senza rovinare esp e poi con esp sugli operatori
  
  for(i=0; i<tmp.size(); i++)
  {
    if(tmp[i]=='+' || tmp[i]=='-') 
    {
      vals.push_back(conv_str(tmp.substr(0, i)));
      tmp=tmp.substr(i+1, (tmp.size()-1));
      i=0;  //questa riga mi è costata venti minuti di duro ragionamento! :|
    }
  }
  
  vals.push_back(conv_str(tmp));  // perchè l'ultimo valore resta escluso... poco male :)
 //ora abbiamo un vector con i numeri, inseriamo gli operatori nell'ordine giusto e il gioco è fatto! 
 
  for(i=0; i<esp.size(); i++)
  {
    if(esp[i]=='+')
      op.push_back('+');
    else if(esp[i]=='-')
      op.push_back('-');
  }  //ora abbiamo un vettore con le operazioni
  
  i=0;
  
  /*do
  {
    if(op[op.size()-i]=='+')
      ris=vals[vals.size()-1]+vals[vals.size()-2];
    else if(op[op.size()-i]=='-')
      ris=vals[vals.size()-1]-vals[vals.size()-2];
    
    vals.erase(vals.end()-1);
    vals.push_back(ris);
    vals.erase(vals.end()-2);
    i++;
    
  } while(vals.size()>1);
  
  * qui mi sono bloccato... non so se ti può essere utile ma se riesci a combinare i due vector per risolvere 
  * parentesi di pari priorità e riesci a estrarre di volta il volta le parentesi più esterne dall'espressione iniziale 
  * e a sostituire per ogni parentesi il suo risultato hai fatto... no? 
  * questo è quello che per ora ho scritto... domani mi ci metto di nuovo.*/
  
  cout<<ris<<endl;
  
  return 0;
}


int conv_str(string s)
{
  int num;
  
  if(s.size()==1)
    num=s[0]-48;
  
  else if(s.size()==2)
    num=((s[0]-48)*10)+(s[1]-48);
 
  else if(s.size()==3)
    num=((s[0]-48)*100)+((s[1]-48)*10)+(s[2]-48);
  
  else if(s.size()==4)
    num=((s[0]-48)*1000)+((s[1]-48)*100)+((s[2]-48)*10)+(s[3]-48);
  
  
  return num;
}

Grazie mille per l'aiuto! Ti do un paio di consigli visto che ci stai provando pure tu.

Il ragionamento sul push_back alla linea 41 ("perchè l'ultimo valore resta escluso") si può fare e non si può fare: in teoria 1*(2+3) dovrebbe essere una stringa valida e con quel sistema si leggerebbe 3) come un numero unico (amenochè lo step di *rimuovere* le parentesi non si faccia prima) e la tua funzione conv_str lo interpreta effettivamente come tale.
Tralasciando il fatto che per ora il tuo codice non legge le parentesi, secondo me la funzione per separare i numeri dalle operazioni dovrebbe essere più indipendente. Il tuo ragionamento è quello di scansare le operazioni, il numero è la parte che rimane, ma secondo me è più semplice cercare i numeri.
Quello che intendo io è qualcosa di simile:
Codice:
int len = 0;
for(i = 0; i <= tmp.size(); i++) 
{
    if(tmp[i] >= '0' && tmp[i] <= '9')
        len++; 

    else if(len > 0)
    {
        vals.push_back(conv_str(tmp.substr(i-len, len))); // di fatto non modifico tmp 
        len = 0;
    }
}

La funzione conv_str funziona meglio così:
Codice:
int conv_str(string s)
{
    int num = 0;
  
    for(size_t it = 0; it < s.length(); it++) // rimane completamente indipendente dalla lunghezza della stringa
    {
        num *= 10;          // inizialmente non fa niente (0*10=0), agli  step successivi shifta a sinistra il numero in base 10 e fa posto 
        num += s[it] - '0'; //  al numero successivo. s[it] - '0' serve  per convertire da ASCII a int un numero formato da una cifra.
    }                       //  esempio: s="123"  ->  0*0=0 0+1=1  ->  1*10=10 10+2=12  ->  12*10=120 120+3=123 

    return num;
}



Comunque ci siamo gente, ora il mio programmino riesce a mangiarsi mostri come questo:
Codice:
>> (3+(5.412*2.123-5.12^4+(5!-7!))+10-2*44*(12+3!^2))/3^3!
expression: (3+(5.412*2.123-5.12^4+(5!-7!))+10-2*44*(12+3!^2))/3^3!
sub_expr = 3+(5.412*2.123-5.12^4+(5!-7!))+10-2*44*(12+3!^2)
sub_expr = 5.412*2.123-5.12^4+(5!-7!)
sub_expr = 5!-7!
sub_expr = 12+3!^2
result: -13.452270
La prima riga è l'input, copiarla su google per credere al risultato :yo:


In ogni caso per tradurlo in codice ASM puoi fermare il compilatore prima che generi l'eseguibile...
Per il g++ credo che sia
g++ -S nomefile.cpp -o nomefile
No?
Sarebbe per un progetto scolastico, se gli porto lì il codice generato da un compilatore se ne accorgono (senza contare che glielo dovrei commentare oralmente). Comunque non mi serve nemmeno in assembly x86, mi serve in assembly MIPS.
 
Grazie mille per l'aiuto! Ti do un paio di consigli visto che ci stai provando pure tu.

Il ragionamento sul push_back alla linea 41 ("perchè l'ultimo valore resta escluso") si può fare e non si può fare: in teoria 1*(2+3) dovrebbe essere una stringa valida e con quel sistema si leggerebbe 3) come un numero unico (amenochè lo step di *rimuovere* le parentesi non si faccia prima) e la tua funzione conv_str lo interpreta effettivamente come tale.
Tralasciando il fatto che per ora il tuo codice non legge le parentesi, secondo me la funzione per separare i numeri dalle operazioni dovrebbe essere più indipendente. Il tuo ragionamento è quello di scansare le operazioni, il numero è la parte che rimane, ma secondo me è più semplice cercare i numeri.
Quello che intendo io è qualcosa di simile:
Codice:
int len = 0;
for(i = 0; i <= tmp.size(); i++) 
{
    if(tmp[i] >= '0' && tmp[i] <= '9')
        len++; 

    else if(len > 0)
    {
        vals.push_back(conv_str(tmp.substr(i-len, len))); // di fatto non modifico tmp 
        len = 0;
    }
}

La funzione conv_str funziona meglio così:
Codice:
int conv_str(string s)
{
    int num = 0;
  
    for(size_t it = 0; it < s.length(); it++) // rimane completamente indipendente dalla lunghezza della stringa
    {
        num *= 10;          // inizialmente non fa niente (0*10=0), agli  step successivi shifta a sinistra il numero in base 10 e fa posto 
        num += s[it] - '0'; //  al numero successivo. s[it] - '0' serve  per convertire da ASCII a int un numero formato da una cifra.
    }                       //  esempio: s="123"  ->  0*0=0 0+1=1  ->  1*10=10 10+2=12  ->  12*10=120 120+3=123 

    return num;
}



Comunque ci siamo gente, ora il mio programmino riesce a mangiarsi mostri come questo:
Codice:
>> (3+(5.412*2.123-5.12^4+(5!-7!))+10-2*44*(12+3!^2))/3^3!
expression: (3+(5.412*2.123-5.12^4+(5!-7!))+10-2*44*(12+3!^2))/3^3!
sub_expr = 3+(5.412*2.123-5.12^4+(5!-7!))+10-2*44*(12+3!^2)
sub_expr = 5.412*2.123-5.12^4+(5!-7!)
sub_expr = 5!-7!
sub_expr = 12+3!^2
result: -13.452270
La prima riga è l'input, copiarla su google per credere al risultato :yo:



Sarebbe per un progetto scolastico, se gli porto lì il codice generato da un compilatore se ne accorgono (senza contare che glielo dovrei commentare oralmente). Comunque non mi serve nemmeno in assembly x86, mi serve in assembly MIPS.
Molto bene! Se hai tutto il sorgente potresti postaro? Se vuoi eh! :)

- - - Updated - - -

E il codice che ho postato doveva essere una parte del codice completo, e lavorare solo su stringhe epurate da parentesi in precedenza!
 
No, non è in fase di compilazione.

Siccome io e l'assembly non andiamo d'accordo ti spiego come farlo in python poi vedi te.

Codice:
espressione=input("Scrivi l'espressione: ")
for elem in espressione:
    try:
        int(elem)
        print(elem + " è un numero!")
    except:
        print(elem + " non è un numero!")

Come puoi vedere dallo screen funziona:

Trma6UQ.png

Ok, non sapevo di questa cosa in python! In effetti funziona, in C++ il discorso è differente! Non credo che un'operazione del genere sia fattibile... Ovviamente potrei sbagliare. [MENTION=156155]St3ve[/MENTION]
 
Visto che è un progetto scolastico, anche se in realtà mi servirebbe solo l'assembly, preferisco non postarlo per adesso. Quando lo consegno rifinisco un po' il sorgente (magari potrei aggiungerci anche le variabili tipo: 7+(i=12*8)+i^2 oppure in un'espressione calcoli una variabile che poi riusi nell'espressione dopo) e lo posto a scopo didattico.
Devo dire che sto progettino, almeno finché sono sul C, mi sta appassionando.
Bene! E' un ottimo progetto, postalo pure in un secondo momento non c'è nessun problema, basta che prima o poi mi mostrerai questa roba :D
E' qualcosa di davvero complesso, molto più di quello che sembra a primo impatto... provai a scrivere un solver per lo studio di funzione che implementava più o meno lo stesso algoritmo ma senza successo... quindi se potessi prendere spunto dal tuo lavoro sarebbe magnifico! ;) Bel lavoro St3ve! Non che sia una sorpresa! E grazie di aver condiviso con noi! :sisi:
 
[MENTION=156155]St3ve[/MENTION]
Bene! Accetto il tuo feedback molto volentieri! Ho trovato qualcosa di interessante anche nelle nuove librerie C++11.
Il codice è un po confuso lo ammetto. Forse devo rivedere un paio di cosette!
Conosci al volo una funzione di conversione float to string?
 
Generalmente il modo migliore per fare conversioni stringhe<->numeri è usando le stringstream, ti permettono anche di convertire stringhe in numeri usando simboli a piacere per definire la virgola e puoi fare altre cose carine.
Ma visto che hai citato il C++11 puoi semplicemente fare:
Codice:
string pippo = std::to_string(12.3456);  // funziona anche con tutti gli altri tipi base
meglio le stringstream! Preferisco compilare in C++ standard! Grazie! Magari se riesco a finire posto la mia versione!
Grazie di tutto e attendiamo novità!
Saluti!
 
Stato
Discussione chiusa ad ulteriori risposte.