为简单的数学公式创建表达式
Create expression for a simple math formula
我对表达式有一些兴趣,出现了一个问题:它引发了一个我没有想到的异常。
我有一个输入 - 简单的数学公式,例如 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
请指教,如何解决?这里我有一个全局参数并引用它所以我不知道为什么它说这些东西。
您的代码存在问题,您有两个 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
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;
}
我对表达式有一些兴趣,出现了一个问题:它引发了一个我没有想到的异常。
我有一个输入 - 简单的数学公式,例如 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
请指教,如何解决?这里我有一个全局参数并引用它所以我不知道为什么它说这些东西。
您的代码存在问题,您有两个 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
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;
}