Créer une expression pour une formule mathématique simple

.net .net-4.5 c# expression expression-trees

Question

Je me suis bien amusé avec Expressions et une question est apparue: elle lève une exception que je ne supposais pas.

J'ai une entrée - une formule mathématique simple, par exemple 2*x+3 , et je veux créer un arbre d'expression pour cela. Alors j'écris ce code

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

mais je reçois une erreur:

Exception non gérée: System.InvalidOperationException: variable 'x' de type 'Sys tem.Double' référencée à partir de scope '', mais n'est pas définie dans System.Linq.Expressions.Compiler.VariableBinder.Reference (nœud d'ions ParameterExpress, stockage VariableStorageKind) sur System.Linq.Expressions.Compiler.VariableBinder.VisitParameter (noeud ParameterEx pression) sur System.Linq.Expressions.ParameterExpression.Accept (visite ExpressionVisitor ou) sur ..., etc.

S'il vous plaît, un conseil, comment peut-il être corrigé? Ici, j'ai un paramètre global unique et le référençant donc je ne sais pas pourquoi il dit ça.

Réponse acceptée

Le problème avec votre code est que vous avez deux instances du paramètre x . L’un d’eux est le champ statique privé qui est utilisé dans la génération d’expression, et le deuxième est que vous créez et utilisez dans la création Lambda.

Si vous avez ParameterExpression, vous devez utiliser la même instance dans expression et transmettre la même instance à la génération Lambda, sinon la procédure échouera comme dans votre exemple.

cela fonctionnera bien si vous supprimez parameterExpression et utiliserez un champ de Parameter privé comme ceci:

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

Exemple de travail dans .NetFiddle - https://dotnetfiddle.net/Onw0Hy


Réponse populaire

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


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow