Qualche tempo fa mi ero soffermato sul problema della risoluzione di espresioni matematiche. il problema non era per per un eigenza quindi non posso dire di avere trovato la soluzione definitiva/valida... quelo che posso fare è postare però gli esempi di codice che avevo prodotto - senza dubbio discutibili.
Prima dipartire mi era detto... arg certo che costruirmi un parser di epressioni matematiche sarebbe un bel casino e certo non ho ne tempo ne voglia, la sluzione migliore sarebbe quella di sfruttare i parser già esistenti. Quali? Quelli del framework! :D
Un primo parser e risolutore adatto per esperssioni relativamente semplici è quello del DataSet. La soluzione non è mia, l'avevo vista in http://www.codearchitects.com/, proposta da Francesco Balena & Co., ma poi ho anche trovato della documentazione su MSDN.
//Risolvere espressioni semplici con uso di variabili
using(DataSet ds = new DataSet())
{
string Expression = "((A + B) * 3) / 2";
DataTable dt = ds.Tables.Add("MyExpression");
dt.Columns.Add("A", Type.GetType("System.Decimal"));
dt.Columns.Add("B", Type.GetType("System.Decimal"));
dt.Columns.Add("Result", Type.GetType("System.Decimal"), Expression);
DataRow dr = dt.Rows.Add(new object[]{1, 2});
Console.WriteLine("{3}; A={1}, B={2} results {0}", dr["Result"], dr["A"], dr["B"], Expression);
}
La soluzione è senza dubbio interessante, tuttavia non andava incontro a casi di espresioni particolarmente complesse con logaritmi, seni - non maliziate ;p - e coseni. La mia idea, con la quale tanto tempo fa avevo risposto sul Forum, è quella di usare il CodeDOM. Ecco il codice che avevo prodotto:
//Idea per come risolvere operazioni piu complesse anche se sarebbe meglio usare CodDom :(
//Potrebbero esserci delle CodeInjection... qndi dipende dall'uso da farsi e da chi è l'utilizzatore finale... etc etc... :p
string Expression = "SIN(45) + 1 + COS(90)";
string CodeExpression = Expression;
Type mt = Type.GetType("System.Math");
string[] MathFunctions = new String[mt.GetMethods().Length];
int i = 0;
foreach(MethodInfo m in mt.GetMethods())
{
MathFunctions[i++] = m.Name;
}
foreach(string MathFunction in MathFunctions)
{
CodeExpression = Regex.Replace(CodeExpression,
MathFunction, string.Format("Math.{0}", MathFunction), RegexOptions.IgnoreCase);
}
StringBuilder sb = new StringBuilder();
sb.Append("using System;\n");
sb.Append("public class MyExpression{\n");
sb.Append("public static double Solve(){\n");
sb.Append("return ").Append(CodeExpression).Append(";\n");
sb.Append("}}");
CSharpCodeProvider provider = new CSharpCodeProvider();
ICodeCompiler comp = provider.CreateCompiler();
CompilerParameters CompParam = new CompilerParameters();
CompParam.GenerateInMemory = true;
CompilerResults CompRes = comp.CompileAssemblyFromSource(CompParam, sb.ToString());
if(CompRes.Errors.Count > 0)
{
foreach(CompilerError ce in CompRes.Errors){
System.Console.WriteLine("{0}: {1}", ce.Line, ce.ErrorText);
}
Type t = CompRes.CompiledAssembly.GetType("MyExpression");
object result = t.InvokeMember("Solve", BindingFlags.InvokeMethod, null, null, null);
Console.WriteLine("{0} = {1}", Expression, result);
Di quest'ultima soluzione la cosa che mi lascia un po perplesso è l'algoritmo per fare il mapping tra la funzioni usate nell'espresione e quelle di System.Math...
Oh si è vero in questa soluzione non sembra esserci posto per le variabili... nella mia testa la solzuione era piu macchinosa... qndo la metto in piedi la bloggo :p
Altra soluzione potrebbe essere qualla di definire la classe MyExpressionBase che definisce tutte le funzioni che si vogliono mettere a disposizione (ovviamante protette). La classe MyExpression che costruiamo ancora al volo questa volta eredita da MyExpressionBase... :p
posted @ venerdì 17 ottobre 2003 12:32