Follow along with the video below to see how to install our site as a web app on your home screen.
Nota: This feature may not be available in some browsers.
Si tratta di individuare nella stringa gli ascii dal 48 al 57, convertirli in interi, individuare nella stringa gli operatori (*, +, -, /) e fare il calcolo... giusto? Non è per nulla facile. Soprattutto se il codice deve contemplare espressioni di lunghezza arbitraria... Io provo, in ogni caso mi interessa sapere come va a finire.Esatto, io ho una stringa che contiene un'espressione aritmetica e mi interessa elaborarla per poter ottenere il risultato.
Se l'errore in è in fase di compilazione (come penso che sia) allora non credo che questa soluzione sia applicabile...In python potresti utilizzare il ciclo for per ogni carattere e trasformarlo in un integer. Se ti dà errore, non è un numero (e poi vai a verificare cosa sia), mentre se non da errore il numero viene trasformato in integer e poi fai le operazioni. (ho fatto una cosa simile non molto tempo fa)
Come si convertono i char di operazione in operatori veri e propri?Penso che si possa risolvere in qualche modo con le Regex.
Il problema sta che in ASM non ho idea di come si possano implementare, in ogni caso si potrebbe fare un ragionamento del genere:
Non so se sia un ragionamento che fila ma potresti fare una MatchCollection delle parentesi EG: "\((.+)\)" e scorri la collection al contrario.
Per i calcoli puoi fare un ciclo per dare la precedenza (si è molto greed ma non mi è venuto in mente altro).
Senza le Regex il discorso è lo stesso, solo che diventa più complicato trovare le parentesi manualmente.
Penso che si possa risolvere in qualche modo con le Regex.
Il problema sta che in ASM non ho idea di come si possano implementare, in ogni caso si potrebbe fare un ragionamento del genere:
Non so se sia un ragionamento che fila ma potresti fare una MatchCollection delle parentesi EG: "\((.+)\)" e scorri la collection al contrario.
Per i calcoli puoi fare un ciclo per dare la precedenza (si è molto greed ma non mi è venuto in mente altro).
Senza le Regex il discorso è lo stesso, solo che diventa più complicato trovare le parentesi manualmente.
In python potresti utilizzare il ciclo for per ogni carattere e trasformarlo in un integer. Se ti dà errore, non è un numero (e poi vai a verificare cosa sia), mentre se non da errore il numero viene trasformato in integer e poi fai le operazioni. (ho fatto una cosa simile non molto tempo fa)
La sostanza è questa:Come si convertono i char di operazione in operatori veri e propri?
if(carattere == '+')
ris = num_left + num_right;
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
string exp;
vector<char> cast;
int i=0;
cout<<"inserisci espressione: "; cin>>exp;
while(i<exp.size())
{
if(exp[i]=='(' || exp[i]==')')
cast.push_back(exp[i]);
i++;
}
cout<<exp<<endl<<endl;
for(i=0; i<cast.size(); i++)
cout<<cast[i]<<" ";
cout<<endl;
return 0;
}
Le parentesi pensavo di gestirle in modo ricorsivo: quando trovo una parentesi aperta inizio a contarle (+1 per quelle aperte e -1 per quelle chiuse) e quando torno a 0 riapplico l'algoritmo di risoluzione sulla sottostringa che parte dopo la prima parentesi e termina prima dell'ultima parentesi trovata, che sarebbe quella che effettivamente chiude la prima parentesi aperta.
Ovviamente per semplificare sto dando per scontato che la stringa in input sia corretta.
if(expression[it] == '(')
{
++brackets; // inizialmente brackets = 0, trovo una parentesi e diventa 1
for(it2 = it+1; it < strlen(expression); ++it2) // it indica la posizione della stringa in cui ho incontrato il carattere '('
{
if(expression[it2] == '(') // se si apre un'altra parentesi, me lo segno
++brackets;
else if(expression[it2] == ')') // se si chiude una parentesi, me lo segno
--brackets;
if(brackets == 0) // se la parentesi che si è chiusa era la corrispondente alla prima
{
sub_expr = malloc(it2 - it); // creo una sottostringa (sotto-espressione)
sub_expr = memcpy(sub_expr, expression+it+1, it2-it-1); // e gli ficco dentro il contenuto delle parentesi
sub_expr[it2-it-1] = '\0';
float_stack_push(&stack_num, evaluate(sub_expr)); // calcolo ricorsivamente (in realtà sono già dentro a evaluate) il risultato della sotto espressione
it = it2; // mi trovavo alla posizione contenente '(', ora salto alla posizione dove c'è ')'
break; // ho trovato la parentesi che mi chiude la '(' che avevo incontrato, stoppo il for
}
}
}
Credo che i casi da contemplare siano talmente tanti... se poi pensi di tradurlo in ASM... è sconfortante
Credo che sarei capace di farlo in C, ma non credo ti aiuterebbe.
Purtroppo non conosco il C
Ah ok, chiaro. Immaginavo fosse un errore di battitura.Scusami, ho editato, volevo scrivere C# la prima volta.
@St3ve
Ok steve l'algoritmo generale mi sebra chiaro, ciò che è difficile è tradurlo...
- trovare tutte le parentesi e le loro posizioni nella stringa
- verificare il contenuto della parentesi più interna
- risolvere
- 1) tradurre la stringa in intero dove ci sono numeri cercando ascii eccetera eccetera
- 2) il rimanente sono sicuramente operazioni dato che non sono né parentesi né numeri
- sostituire il risultato al posto della parentesi nella stringa... -> nuova stringa
- loop
Eppure manca qualcosa
Two stacks are required, one for numbers and the other for operators. The algorithm is:
- For each item in the infix expression (no parens) from the right to the left
- If the item is a number then push it on the number stack.
- If the item is an operator (+,-,*, or /) and: the operator stack is empty or the operator on the top of the stack is higher in priority (* and / are higher in priority than + or -), then
- Pop an operator from the operator stack.
- Pop two numbers off the number stack.
- Calculate using second number-operator-first number.
- Push the result on the number stack.
- Push the item on the operator stack.
- Else push the item on the operator stack.
- After the loop, while the operator stack is not empty
- Pop an operator from the operator stack.
- Pop two numbers off the number stack.
- Calculate using second number-operator-first number.
- Push the result on the number stack.
- The answer is the last item in the number stack.
/* 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;
}
@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; }
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;
}
}
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;
}
>> (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
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.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?
Molto bene! Se hai tutto il sorgente potresti postaro? Se vuoi eh!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:
La prima riga è l'input, copiarla su google per credere al risultatoCodice:>> (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
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.
Se l'errore in è in fase di compilazione (come penso che sia) allora non credo che questa soluzione sia applicabile...
- - - Updated - - -
Come si convertono i char di operazione in operatori veri e propri?
espressione=input("Scrivi l'espressione: ")
for elem in espressione:
try:
int(elem)
print(elem + " è un numero!")
except:
print(elem + " non è un numero!")
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:
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. @St3ve
string esp;
cout<<"espressione = ";
cin>>esp;
size_t pos = 0;
int i;
while(esp.length())
{
try
{
i = stoi(esp, &pos);
cout << "e' il numero = " << i << endl;
esp = esp.substr(pos);
}
catch(...)
{
cout << "non e' un numero" << endl;
esp = esp.substr(pos-1);
}
}
espressione = 12pippo34ciccio56
e' il numero = 12
non e' un numero
non e' un numero
non e' un numero
non e' un numero
non e' un numero
e' il numero = 34
non e' un numero
non e' un numero
non e' un numero
non e' un numero
non e' un numero
non e' un numero
e' il numero = 56
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.Molto bene! Se hai tutto il sorgente potresti postaro? Se vuoi eh!
Bene! E' un ottimo progetto, postalo pure in un secondo momento non c'è nessun problema, basta che prima o poi mi mostrerai questa robaVisto 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.