2009-09-17 5 views
17

Có rất nhiều thuật toán để đánh giá biểu thức, ví dụ:tốt nhất và con đường ngắn nhất để đánh giá biểu thức toán học

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

Có cách nào để đánh giá bất kỳ biểu thức toán học sử dụng phản xạ C# .net hoặc công nghệ .net hiện đại khác?

+0

Tôi hỏi một câu hỏi tương tự một khi trở lại. Bạn có thể muốn xem xét một số câu trả lời sau: http://stackoverflow.com/questions/234217/is-it-possible-to-translate-a-user-entered-mathematical-equation-into-c-code-at – raven

+0

Bạn đã tìm thấy cách liên kết đến các biến được sử dụng trong phần còn lại của mã "tĩnh/được biên dịch trước" chưa? –

Trả lời

19

Ngoài câu trả lời của Thomas, thực sự có thể truy cập thư viện JScript (không được chấp nhận) trực tiếp từ C#, có nghĩa là bạn có thể sử dụng hàm tương đương với hàm J2 eval của 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

Xấu hổ nó không hỗ trợ dấu mũ^cho lũy thừa. –

13

Chắc chắn là có thể. Lớp CodeSnippetCompileUnit về cơ bản này. Tôi đã viết cho bạn một số mã sử dụng ví dụ. Bạn sẽ cần phải bao gồm các không gian tên này:

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

Dưới đây là các mã:

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

Thay thế 'thông số' và 'biểu hiện' bởi bất cứ điều gì, và bạn đã có cho mình một đánh giá biểu hiện chung.

Nếu bạn nhận được một FileNotFoundException trong kết quả.CompiledAssembly, sau đó đoạn mã không biên dịch được.

Bạn cũng có thể muốn xem xét lớp System.CodeDom.CodeSnippetExpression. Nó được sử dụng để đọc cụ thể hơn các biểu thức, nhưng một biểu thức của chính nó không thể được biên dịch, vì vậy bạn sẽ cần phải sử dụng CodeDom hơn để xây dựng một lớp và phương thức làm việc xung quanh nó. Điều này rất hữu ích nếu bạn muốn có thể thao tác theo chương trình loại lớp bạn đang tạo ra. CodeSnippetCompileUnit là tốt đẹp để tạo ra một lớp làm việc toàn bộ cùng một lúc (và đơn giản hơn cho một ví dụ) nhưng để thao tác nó, bạn sẽ phải thực hiện các thao tác chuỗi bất tiện.

+0

giải pháp tốt nhất. –

+0

Đối với hồ sơ, hiệu suất của giải pháp này khi sử dụng ncalc là HUGE, tôi đã thử nghiệm nó cho một nhà soạn thảo và một số chức năng đa biến mất hơn 500s để được biểu quyết, với điều này tôi mất ít hơn 5 lần trên 400.000 điểm. Giải pháp tuyệt vời! –

3

Mặc dù sử dụng dịch vụ trình biên dịch là một giải pháp đơn giản và hiệu quả, nó đưa ra các vấn đề bảo mật nghiêm trọng nếu biểu thức được người dùng nhập vì nó có thể thực hiện hầu như mọi thứ.

Có một giải pháp rất đơn giản khác an toàn hơn nhiều: tận dụng chức năng JScript Eval. Bạn chỉ cần làm theo các bước sau:

Tạo một file js tên JsMath.js:

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

Biên dịch nó thành một thư viện lớp:

jsc /t:library JsMath.js 

tham khảo thư viện JsMath trong dự án # C và sử dụng nó như thế:

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

Tôi thậm chí không bao giờ được coi là an ninh, cũng như tôi không biết về hàm JScript eval. Đây cũng là cách ngắn gọn hơn giải pháp của tôi. Câu trả lời tốt! – Joren

+0

Thực sự có thể truy cập chức năng 'eval' trực tiếp từ C#, không có bước biên dịch JScript trung gian. Xem câu trả lời của tôi để biết chi tiết. – LukeH

+0

Để tránh các sự cố bảo mật khi sử dụng các dịch vụ trình biên dịch, tôi sử dụng ANTL để phân tích cú pháp biểu thức người dùng trước và tránh bất kỳ đầu vào lạ nào. Nếu bạn đang tìm kiếm hiệu suất, hàm 'eval()' có thể không hoạt động. –

3

Đối với tôi, Vici.Parser hoạt động rất tốt: check it out here, đó là trình phân tích biểu thức linh hoạt nhất mà tôi đã tìm thấy từ trước đến nay.

(chúng tôi đã sử dụng nó để thiết lập 'con người có thể đọc được' quy tắc kinh doanh, với dữ liệu được cung cấp bởi một cơ sở dữ liệu SQL server)

Các ví dụ có sẵn và có một sự hỗ trợ rất tốt bởi nhà phát triển (kiểm tra trang web của diễn đàn).

+0

Trông rất thú vị. – NotMe

+1

@Roel - Liên kết đã chết. –

3

ncalc là tốt nhất. bạn có thể tìm thấy nó trong codeplex cũng trong nugget.
NCalc là một trình đánh giá biểu thức toán học trong .NET. NCalc có thể phân tích bất kỳ biểu thức nào và đánh giá kết quả, bao gồm các tham số tĩnh hoặc động và các hàm tùy chỉnh.

1

Tôi nghĩ đây là cách tốt nhất. Petar Repac's answer thật tuyệt vời. Sử dụng 'biểu hiện' lập luận của đối tượng DataColumn giải quyết vô cùng dễ dàng và topic:

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