2009-09-17 8 views
17

Ci sono molti algoritmi per valutare le espressioni, per esempio:modo migliore e più breve per valutare le espressioni matematiche

  1. By Recursive Descent
  2. Shunting-yard algorithm
  3. Reverse Polish notation

Esiste un modo per valutare qualsiasi espressione matematica usando la riflessione C# .net o altra tecnologia moderna .net?

+0

Ho fatto una domanda simile un po 'indietro. Si consiglia di guardare alcune di queste risposte: http://stackoverflow.com/questions/234217/is-it-possible-to-translate-a-user-entered-mathematical-equation-into-c-code-at – raven

+0

Hai trovato un modo per collegare le variabili utilizzate nel resto del codice "statico/precompilato"? –

risposta

19

Oltre alla risposta di Thomas, è effettivamente possibile accedere alle (ridotte) librerie JScript direttamente da C#, il che significa che è possibile utilizzare l'equivalente della funzione eval di JScript.

using Microsoft.JScript;  // needs a reference to Microsoft.JScript.dll 
using Microsoft.JScript.Vsa; // needs a reference to Microsoft.Vsa.dll 

// ... 

string expr = "7 + (5 * 4)"; 
Console.WriteLine(JScriptEval(expr)); // displays 27 

// ... 

public static double JScriptEval(string expr) 
{ 
    // error checking etc removed for brevity 
    return double.Parse(Eval.JScriptEvaluate(expr, _engine).ToString()); 
} 

private static readonly VsaEngine _engine = VsaEngine.CreateEngine(); 
+0

Peccato che non supporti il ​​cursore^per l'esponenziazione. –

13

È certamente possibile. La classe CodeSnippetCompileUnit fa fondamentalmente questo. Ti ho scritto un esempio di codice d'uso. Dovrai includere questi spazi dei nomi:

  • System.CodeDom.Compiler;
  • System.CodeDom;
  • Microsoft.CSharp;
  • System.Reflection;

Ecco il codice:

string source = @" 
class MyType 
{ 
    public static int Evaluate(<!parameters!>) 
    { 
     return <!expression!>; 
    } 
} 
"; 

string parameters = "int a, int b, int c"; 
string expression = "a + b * c"; 

string finalSource = source.Replace("<!parameters!>", parameters).Replace("<!expression!>", expression); 

CodeSnippetCompileUnit compileUnit = new CodeSnippetCompileUnit(finalSource); 
CodeDomProvider provider = new CSharpCodeProvider(); 

CompilerParameters parameters = new CompilerParameters(); 

CompilerResults results = provider.CompileAssemblyFromDom(parameters, compileUnit); 

Type type = results.CompiledAssembly.GetType("MyType"); 
MethodInfo method = type.GetMethod("Evaluate"); 

// The first parameter is the instance to invoke the method on. Because our Evaluate method is static, we pass null. 
int result = (int)method.Invoke(null, new object[] { 4, -3, 2 }); 

replace 'parametri' e 'l'espressione' di qualsiasi altra cosa, e ti sei preso un valutatore generale espressione.

Se si ottiene un oggetto FileNotFoundException in results.CompiledAssembly, lo snippet non è stato compilato.

Si potrebbe anche voler dare un'occhiata alla classe System.CodeDom.CodeSnippetExpression. Viene utilizzato per leggere le espressioni in modo più specifico, ma un'espressione di per sé non può essere compilata, quindi è necessario utilizzare più CodeDom per creare una classe di lavoro e un metodo attorno ad esso. Questo è utile se vuoi essere in grado di manipolare a livello di codice il tipo di classe che stai generando. CodeSnippetCompileUnit è bello generare un'intera classe di lavoro in una volta (e più semplice per un esempio) ma per manipolarlo si dovrebbero fare manipolazioni di stringa inopportune.

+0

la soluzione migliore. –

+0

Per la cronaca, le prestazioni di questa soluzione rispetto all'utilizzo di ncalc sono ENORME, l'ho provata per un grapher e alcune funzioni a più variabili hanno richiesto più di 500s per essere plottato, con questo mi ci sono voluti meno di 5 secondi per oltre 400.000 punti. Ottima soluzione! –

3

Anche se l'utilizzo dei servizi di compilazione è una soluzione semplice ed efficiente, solleva seri problemi di sicurezza se l'utente viene inserito nell'espressione, poiché può eseguire virtualmente.

C'è un'altra soluzione molto semplice che è molto più sicura: sfruttare la funzione JScript Eval. Hai solo bisogno di seguire questi passaggi:

creare un file js nome JsMath.js:

class JsMath 
{ 
    static function Eval(expression : String) : double 
    { 
     return eval(expression); 
    }; 
} 

compilarlo in una libreria di classi:

jsc /t:library JsMath.js 

Riferimento biblioteca jsMath nel progetto C# , e usarlo come quella:

double result = JsMath.Eval(expression); 
+0

Non ho mai considerato la sicurezza, né sapevo della funzione di valutazione JScript. Anche questo è molto più conciso della mia soluzione. Buona risposta! – Joren

+0

In realtà è possibile accedere alla funzione 'eval' direttamente da C#, senza il passo di compilazione JScript intermedio. Vedi la mia risposta per i dettagli. – LukeH

+0

Per evitare problemi di sicurezza utilizzando i servizi del compilatore, utilizzo ANTL per analizzare l'espressione dell'utente ed evitare qualsiasi input strano. Se stai cercando prestazioni, la funzione 'eval()' potrebbe non funzionare. –

3

Per me Vici.Parser funziona molto bene: check it out here, è il parser di espressioni più flessibile che ho trovato finora.

(abbiamo usato per impostare 'leggibile' regole di business, con i dati forniti da un database del server SQL)

Gli esempi sono disponibili e c'è un ottimo supporto da parte dello sviluppatore (controllare il sito web del Forum).

+0

Sembra molto interessante. – NotMe

+1

@Roel - Il collegamento è morto. –

3

ncalc è il migliore. lo puoi trovare in codeplex anche in nugget.
NCalc è un analizzatore di espressioni matematiche in .NET. NCalc può analizzare qualsiasi espressione e valutare il risultato, inclusi parametri statici o dinamici e funzioni personalizzate.

1

Penso che questo sia il modo migliore di tutti. Petar Repac's answer è incredibile. Utilizzando l'argomento 'espressione' dell'oggetto DataColumn risolve incredibilmente semplice e veloce l'argomento:

static double Evaluate(string expression) 
{ 
    var loDataTable = new DataTable(); 
    var loDataColumn = new DataColumn("Eval", typeof(double), expression); 
    loDataTable.Columns.Add(loDataColumn); 
    loDataTable.Rows.Add(0); 
    return (double)(loDataTable.Rows[0]["Eval"]); 
}