2015-05-21 59 views
1

我有一些有趣的表現形式和出現的問題創建表達式:它拋出,我沒有想例外。一個簡單的數學公式

我有一個輸入 - 簡單的數學公式,例如2*x+3,我想爲它創建一個表達式樹。所以,我寫這篇文章的代碼

using System; 
using System.Linq.Expressions; 

namespace ConsoleApplication50 
{ 
    class Program 
    { 
     static void Main() 
     { 
      string s = "3*x^2+2/5*x+4"; 
      Expression<Func<double, double>> expr = MathExpressionGenerator.GetExpression(s); 
      Console.WriteLine(expr); 

      var del = expr.Compile(); 

      Console.WriteLine(del(10)); 
     } 


    } 

    static class MathExpressionGenerator 
    { 
     public const string SupportedOps = "+-*/^"; 
     private static readonly ParameterExpression Parameter = Expression.Parameter(typeof(double), "x"); 

     public static Expression<Func<double, double>> GetExpression(string s) 
     { 
      ParameterExpression parameterExpression = Expression.Parameter(typeof(double), "x"); 
      Expression result = GetExpressionInternal(s); 
      return Expression.Lambda<Func<double, double>>(result, parameterExpression); 
     } 

     private static Expression GetExpressionInternal(string s) 
     { 
      double constant; 
      if (s == "x") 
       return Parameter; 
      if (double.TryParse(s, out constant)) 
       return Expression.Constant(constant, typeof(double)); 
      foreach (char op in SupportedOps) 
      { 
       var split = s.Split(new[] { op }, StringSplitOptions.RemoveEmptyEntries); 
       if (split.Length > 1) 
       { 
        var expression = GetExpressionInternal(split[0]); 
        for (int i = 1; i < split.Length; i++) 
        { 
         expression = RunOp(expression, GetExpressionInternal(split[i]), op); 
        } 
        return expression; 
       } 
      } 
      throw new NotImplementedException("never throws"); 
     } 

     private static Expression RunOp(Expression a, Expression b, char op) 
     { 
      switch (op) 
      { 
       case '+': 
        return Expression.Add(a, b); 
       case '-': 
        return Expression.Subtract(a, b); 
       case '/': 
        return Expression.Divide(a, b); 
       case '*': 
        return Expression.Multiply(a, b); 
       case '^': 
        return Expression.Power(a, b); 
      } 
      throw new NotSupportedException(); 
     } 
    } 
} 

,但我得到一個錯誤:

Unhandled Exception: System.InvalidOperationException: variable 'x' of type 'Sys tem.Double' referenced from scope '', but it is not defined at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpress ion node, VariableStorageKind storage) at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterEx pression node) at System.Linq.Expressions.ParameterExpression.Accept(ExpressionVisitor visit or) at ... and so on

請,諮詢,怎麼能解決嗎?在這裏我有一個單一的全局參數,並引用它,所以我不知道,爲什麼它說這個東西。

回答

2

問題與您的代碼是,你有x參數的兩個實例。其中一個是用於跨表達式生成的私有靜態字段,第二個是您在Lambda創建中創建和使用的字段。

如果你有ParameterExpression,那麼你應該在表達式中使用相同的實例,並通過同一個實例入λ代,否則將無法像在你的榜樣。

它會正常工作,如果你刪除parameterExpression並且將使用私人Parameter領域是這樣的:在.NetFiddle

public static Expression<Func<double, double>> GetExpression(string s) 
{ 
    Expression result = GetExpressionInternal(s); 
    return Expression.Lambda<Func<double, double>>(result, Parameter); 
} 

工作的例子 - https://dotnetfiddle.net/Onw0Hy

+0

哎呀我的複製粘貼,我沒有」 t引用單個對象。這顯然是造成這種行爲的原因。感謝您的幫助 –

+0

@AlexZhukovskiy:我用你的代碼,它的偉大工程用簡單的公式,但如何擴展代碼來支持括號:'()'和''e' Math.Expr'。我嘗試過,但我只得到例外 –

+0

@PaulMeems這並不容易,使其與括號工作,因爲他們正在改變優先級,而我只是用'string.Split'而忽略它分裂。你基本上必須從頭開始重寫它。你可以嘗試首先打開括號(從3 *(x + 2)轉換到3 * x + 3 * 2,然後運行這個人。這可能比編寫嚴肅的解析器容易。 –

0
static void Main(string[] args) 
{ 
     var str = @"3*x^2+2/5*x+4"; 
     str = Transform(str); 
     var param = Expression.Parameter(typeof (double), "x"); 
     var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] {param}, null, str); 
     var exp10 = expression.Compile().DynamicInvoke(10); 
     Console.WriteLine(exp10); 
} 
    public const string SupportedOps = "+-*/";//separators without^
    private static string Transform(string expression) 
    { 
     //replace x^y with Math.Pow(x,y) 
     var toBeReplaced = expression.Split(SupportedOps.ToCharArray()).Where(s => s.Contains("^")); 
     var result = expression; 
     return toBeReplaced.Aggregate(expression, (current, str) => current.Replace(str, string.Format("Math.Pow({0})", str.Replace('^', ',')))); 
     //OR 
     //foreach (var str in toBeReplaced) 
     //{ 
     // result =result.Replace(str, string.Format("Math.Pow({0})", str.Replace('^', ','))); 
     //} 
     //return result;  
}