2009-09-17 38 views
17
最短的路

有很多種算法來計算表達式,例如:貝斯特和評估數學表達式

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

有什麼辦法來評估任何數學表達式使用C#.net反射或其他現代.NET技術?

+0

我問過類似的問題而回。你可能想看看這些答案:http://stackoverflow.com/questions/234217/is-it-possible-to-translate-a-user-entered-mathematical-equation-into-c-code-at – raven 2009-09-17 12:01:28

+0

你有沒有找到一種方法來鏈接到其他「靜態/預編譯」代碼中使用的變量? – 2010-11-12 09:31:49

回答

19

繼托馬斯的答案,它實際上可以直接從C#,這意味着你可以使用JScript中的eval功能相當於訪問(不推薦)的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

羞愧它不支持插入符^指數。 – 2015-12-20 00:00:58

13

這當然有可能。 CodeSnippetCompileUnit類基本上這樣做。 我給你寫了一些示例用法代碼。您需要包含這些命名空間:

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

下面的代碼:

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 }); 

替換「參數」,利用一切「表達」,你也得爲自己的一般表達式求值。

如果您在result.CompiledAssembly中發現FileNotFoundException,則代碼段無法編譯。

您可能還想看看System.CodeDom.CodeSnippetExpression類。它用於更具體地閱讀表達式,但表達式本身不能被編譯,所以您需要使用更多的CodeDom來構建工作類和方法。如果你想以編程方式操作你正在生成的類,這很有用。 CodeSnippetCompileUnit很好地同時生成一個完整的工作類(並且對於一個例子來說更簡單),但要操縱它,你必須執行不方便的字符串操作。

+0

最好的解決方案。 – 2014-01-03 12:52:49

+0

爲了記錄,這個解決方案使用ncalc的性能是巨大的,我測試了它的一個繪圖儀和一些多變量函數超過500s被繪製,這使我花了不到5s繪製超過400,000點。好的解決方案 – 2018-01-30 01:55:33

3

儘管使用編譯器服務是一種簡單高效的解決方案,但如果表達式是由用戶輸入的話,則會引發嚴重的安全問題,因爲它可以執行任何東西。

還有另一個非常簡單的解決方案更安全:利用JScript Eval函數。你只需要按照下列步驟操作:

創建一個js文件名爲JsMath.js:

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

它編譯成一個類庫:

jsc /t:library JsMath.js 

參考的JsMath庫在C#項目,並使用它:

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

我從來沒有考慮過安全問題,也不了​​解JScript eval函數。這也比我的解決方案更簡潔。好答案! – Joren 2009-09-17 13:16:33

+0

實際上可以直接從C#訪問'eval'函數,而不需要中間的JScript編譯步驟。詳情請參閱我的回答。 – LukeH 2009-09-17 13:19:06

+0

爲了避免使用編譯器服務的安全問題,我使用ANTL預解析用戶表達式並避免任何奇怪的輸入。如果你正在尋找性能,'eval()'函數可能不起作用。 – 2018-01-30 02:05:33

3

對我Vici.Parser工作得非常好:check it out here,它是迄今爲止我發現的最靈活的表達式解析器。

(我們使用它來建立「人類可讀」的業務規則,由一個SQL Server數據庫提供的數據)

範例,並有由開發商是一個很好的支持(檢查網站論壇)。

+0

看起來很有趣。 – NotMe 2010-12-09 16:03:42

+1

@Roel - 鏈接已死。 – 2014-05-21 17:22:11

3

ncalc是最好的。你也可以在codeplex中找到它。
NCalc是.NET中的數學表達式求值器。 NCalc可以解析任何表達式並評估結果,包括靜態或動態參數和自定義函數。

1

我認爲這是所有人的最佳方式。 Petar Repac's answer是驚人的。 使用DataColumn對象的「表達」的說法令人難以置信的解決,容易的題目是:

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"]); 
}