Molti anni fa, avevo scritto in GWbasic, un parser per valutare un'espressione matematica che rispetta le priorità di calcolo delle 4 operazioni matematiche di base + - * /
Per diletto l'ho convertito in C#, un lavoretto da pochi minuti.
Poi mi son detto, ora scrivo un nuovo parser simile ma usando le peculiarità di C# che conosco, altro lavoretto da pochi minuti.
Le due void accettano in ingresso una srtinga che contiene un'espressione matematica con i numeri spaziati dagli operatori, ad ogni calcolo eseguito mostro a schermo la nuova espressione generata.
Se si scrive l'espressione correttamente tutto funziona, ma volevo migliorare l'input permettendo all'utente di scrivere l'espressione come meglio crede, esempio:
ora se la stringa di ingresso contiene: "12 - 2 * -4 + 12 / 6" tutto funziona correttamente
come fare per normalizzare una stringa del tipo: "12-2*-4+12/6" ?
o peggio: "12 -2* -4+ 12/6" ?
all'inizio ingenuamente avevo immesso un correttivo:
// Aggiusto gli spazi dell'espressione immessa per compatibilizzarla con l'algoritmo di valutazione
espressione = espressione.Replace(" ", "");
espressione = espressione.Replace("+", " + ").Replace("-", " - ").Replace("*", " * ").Replace("/", " / ");
ma tale approcio funziona solo se non si immettono numeri negativi
Posto il codice completo realizzato e aspetto i vostri consigli anche su un diverso approcio per realizzare un parser di espressione matematica.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Valutazione_espressione
{
class Program
{
static void Main(string[] args)
{
string espressione = "";
riinput:
Console.WriteLine("\nScrivi un'espressione matematica, operazioni matematiche ammesse + - * /");
Console.WriteLine("Esempio: -2,5 * 8 * 25,6 / 3 - -8,6");
Console.Write("\nEspressione immessa: ");
espressione = Console.ReadLine();
// Aggiusto gli spazi dell'espressione immessa per compatibilizzarla con l'algoritmo di valutazione
//espressione = espressione.Replace(" ", "");
//espressione = espressione.Replace("+", " + ").Replace("-", " - ").Replace("*", " * ").Replace("/", " / ");
Console.WriteLine("\n-----------------> GWBasic");
CalcolaEspressioneBASIC(espressione);
Console.WriteLine("\n-----------------> C#");
CalcolaEspressioneNET(espressione);
goto riinput;
}
static void CalcolaEspressioneBASIC(string sEspressione)
{ // accetta in ingresso un'espressione matematica con 1 spazio tra numeri e operatore
// simboli matematici ammessi + - * /
// Esempio espressione valida: 10 + 20 / 56 - 38.....
// mio codice originale GWBasic, adattato in C#, cambiando il meno possibile
string sInputElaborato = sEspressione;
Console.WriteLine(sInputElaborato);
int pos1 = 0;
int pos2 = 0;
int pos3 = 0;
int ciclo = 0;
double a1 = 0;
double a2 = 0;
double Risultato = 0;
string sCarattere = null;
string sOp = "";
//-----------------------------------------------------
// elaborazione moltiplicazioni e divisioni in sequenza
//-----------------------------------------------------
do
{ // trovo la prima moltiplicazione o la divisione
pos2 = 0;
for (ciclo = 1; ciclo < sInputElaborato.Length; ciclo++)
{
if (sInputElaborato.Substring(ciclo - 1, 2) == "* ")
{
pos2 = ciclo;
sOp = "*";
break;
}
if (sInputElaborato.Substring(ciclo - 1, 2) == "/ ")
{
pos2 = ciclo;
sOp = "/";
break;
}
}
//-----------------------------------------------------------------------
if (pos2 == 0) { break; } // non ci sono piu' moltiplicazioni e divisioni
//-----------------------------------------------------------------------
// trovo lo spazio successivo a partire da pos2
pos3 = sInputElaborato.Length + 1;
for (ciclo = pos2 + 2; ciclo < sInputElaborato.Length; ciclo++)
{
sCarattere = sInputElaborato.Substring(ciclo - 1, 1); // divido i caratteri digitati uno ad uno
if (sCarattere == " ")
{
pos3 = ciclo;
break;
}
}
// trovo lo spazio precedente a partire da pos2
pos1 = 0;
for (ciclo = pos2 - 2; ciclo >= 1; ciclo--)
{
sCarattere = sInputElaborato.Substring(ciclo - 1, 1); // divido i caratteri digitati uno ad uno
if (sCarattere == " ")
{
pos1 = ciclo;
break;
}
}
// ora sono in grado di fare la moltiplicazione o la divisione
a1 = Convert.ToDouble(sInputElaborato.Substring(pos1, pos2 - pos1 - 1));
a2 = Convert.ToDouble(sInputElaborato.Substring(pos2, pos3 - pos2 - 1));
if (sOp == "*")
{
Risultato = a1 * a2;
}
else
{
Risultato = a1 / a2;
}
// ricompongo la stringa sostituendo il risultato della moltiplicazione o divisione
sInputElaborato = sInputElaborato.Substring(0, pos1) + Risultato.ToString() + sInputElaborato.Substring(pos3 - 1);
Console.WriteLine(sInputElaborato);
} while (true);
//---------------------------------------------
// elaborazione somme e sottrazioni in sequenza
//---------------------------------------------
do
{
// trovo la prima sottrazione o la prima somma
pos2 = 0;
for (ciclo = 1; ciclo < sInputElaborato.Length; ciclo++)
{
if (sInputElaborato.Substring(ciclo - 1, 2) == "- ")
{
pos2 = ciclo;
sOp = "-";
break;
}
if (sInputElaborato.Substring(ciclo - 1, 2) == "+ ")
{
pos2 = ciclo;
sOp = "+";
break;
}
}
//--------------------------------------------------------------
if (pos2 == 0) { break; }// non ci sono piu' somme o sottrazioni
//--------------------------------------------------------------
// trovo lo spazio successivo a partire da pos2
pos3 = sInputElaborato.Length + 1;
for (ciclo = pos2 + 2; ciclo < sInputElaborato.Length; ciclo++)
{
sCarattere = sInputElaborato.Substring(ciclo - 1, 1); // divido i caratteri digitati uno ad uno
if (sCarattere == " ")
{
pos3 = ciclo;
break;
}
}
// trovo lo spazio precedente a partire da pos2
pos1 = 0;
for (ciclo = pos2 - 2; ciclo >= 1; ciclo--)
{
sCarattere = sInputElaborato.Substring(ciclo - 1, 1); // divido i caratteri digitati uno ad uno
if (sCarattere == " ")
{
pos1 = ciclo;
break;
}
}
// ora sono in grado di fare la somma o la sottrazione
a1 = Convert.ToDouble(sInputElaborato.Substring(pos1, pos2 - pos1 - 1));
a2 = Convert.ToDouble(sInputElaborato.Substring(pos2, pos3 - pos2 - 1));
if (sOp == "-")
{
Risultato = a1 - a2;
}
else
{
Risultato = a1 + a2;
}
// ricompongo la stringa sostituendo il risultato della sottrazione o somma
sInputElaborato = sInputElaborato.Substring(0, pos1) + Risultato.ToString() + sInputElaborato.Substring(pos3 - 1);
Console.WriteLine(sInputElaborato);
} while (true);
} //************************************************************************************
//************************************************************************************
static void CalcolaEspressioneNET(string sEspressione)
{ // accetta in ingresso un'espressione matematica con 1 spazio tra numeri e operatore
// simboli matematici ammessi + - * /
// Esempio espressione valida: 10 + 20 / 56 - 38.....
// mio codice odierno C#
double risultato;
List<string> DatiDivisi;
DatiDivisi = sEspressione.Split(" ").ToList();
Console.WriteLine(string.Join(" ", DatiDivisi));
//-----------------------------------------------------
// elaborazione moltiplicazioni e divisioni in sequenza
//-----------------------------------------------------
do
{
for (int ciclo = 0; ciclo < DatiDivisi.Count; ciclo++)
{
if (DatiDivisi[ciclo] == "*")
{
risultato = Convert.ToDouble(DatiDivisi[ciclo - 1]) * Convert.ToDouble(DatiDivisi[ciclo + 1]);
SostituisceCalcolo(ref DatiDivisi, ciclo, risultato);
break;
}
if (DatiDivisi[ciclo] == "/")
{
risultato = Convert.ToDouble(DatiDivisi[ciclo - 1]) / Convert.ToDouble(DatiDivisi[ciclo + 1]);
SostituisceCalcolo(ref DatiDivisi, ciclo, risultato);
break;
}
}
} while (DatiDivisi.Contains("*") == true | DatiDivisi.Contains("/") == true);
//---------------------------------------------
// elaborazione somme e sottrazioni in sequenza
//---------------------------------------------
do
{
for (int ciclo = 0; ciclo < DatiDivisi.Count; ciclo++)
{
if (DatiDivisi[ciclo] == "+")
{
risultato = Convert.ToDouble(DatiDivisi[ciclo - 1]) + Convert.ToDouble(DatiDivisi[ciclo + 1]);
SostituisceCalcolo(ref DatiDivisi, ciclo, risultato);
break;
}
if (DatiDivisi[ciclo] == "-")
{
risultato = Convert.ToDouble(DatiDivisi[ciclo - 1]) - Convert.ToDouble(DatiDivisi[ciclo + 1]);
SostituisceCalcolo(ref DatiDivisi, ciclo, risultato);
break;
}
}
} while (DatiDivisi.Contains("+") == true | DatiDivisi.Contains("-") == true);
}
static void SostituisceCalcolo(ref List<string> lista, int indice, double sostituto)
{
lista[indice] = sostituto.ToString();
lista.RemoveAt(indice + 1);
lista.RemoveAt(indice - 1);
Console.WriteLine(string.Join(" ", lista));
}
}
}