【问题标题】:Create expression for a simple math formula为简单的数学公式创建表达式
【发布时间】:2015-05-21 22:39:15
【问题描述】:

我对 Expressions 有一些乐趣,然后出现了一个问题:它引发了一个我没想到的异常。

我有一个输入 - 简单的数学公式,例如 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();
        }
    }
}

但我得到一个错误:

未处理的异常:System.InvalidOperationException:变量“x” 从范围“”引用的类型“System.Double”,但未定义 在 System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpress 离子节点,VariableStorageKind 存储)在 System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterEx 压节点)在 System.Linq.Expressions.ParameterExpression.Accept(ExpressionVisitor 访问或)在...等等

请教,如何解决?在这里,我有一个全局参数并引用它,所以我不知道为什么它会这么说。

【问题讨论】:

    标签: c# .net expression .net-4.5 expression-trees


    【解决方案1】:

    您的代码的问题是您有两个 x 参数实例。其中之一是用于跨表达式生成的私有静态字段,第二个是您在创建 Lambda 时创建和使用的。

    如果您有 ParameterExpression,那么您应该在表达式中使用相同的实例并将相同的实例传递给 Lambda 生成,否则它将像您的示例一样失败。

    如果您删除 parameterExpression 并使用私有 Parameter 字段,它将正常工作:

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

    .NetFiddle 中的工作示例 - https://dotnetfiddle.net/Onw0Hy

    【讨论】:

    • 该死的复制粘贴,我没有引用单个对象。这显然是导致这种行为的原因。感谢您的帮助
    • @AlexZhukovskiy:我正在使用您的代码,它适用于简单的公式,但如何扩展代码以支持括号:()e Math.Expr。我试过了,但只有例外
    • @PaulMeems 使用括号并不容易,因为它们正在改变优先级,而我只是用忽略它的string.Split 来划分。你基本上必须从头开始重写它。你可以先尝试开括号(从3*(x+2)转为3*x+3*2,然后运行这个家伙。可能比写认真的解析器容易。
    【解决方案2】:
    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;    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-23
      相关资源
      最近更新 更多