C# 为简单的数学公式创建表达式
我对表达式很感兴趣,出现了一个问题:它抛出了一个我没有想到的异常 我有一个输入-简单的数学公式,例如C# 为简单的数学公式创建表达式,c#,.net,expression,.net-4.5,expression-trees,C#,.net,Expression,.net 4.5,Expression Trees,我对表达式很感兴趣,出现了一个问题:它抛出了一个我没有想到的异常 我有一个输入-简单的数学公式,例如2*x+3,我想为它创建一个表达式树。所以我写了这个代码 using System; using System.Linq.Expressions; namespace ConsoleApplication50 { class Program { static void Main() { string s = "3*x^2+2
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.Linq.Expressions;
命名空间控制台应用程序50
{
班级计划
{
静态void Main()
{
字符串s=“3*x^2+2/5*x+4”;
表达式expr=MathExpressionGenerator.GetExpression;
控制台写入线(expr);
var del=expr.Compile();
控制台写入线(del(10));
}
}
静态类MathExpressionGenerator
{
public const string SupportedOps=“+-*/^”;
私有静态只读参数Expression Parameter=Expression.Parameter(typeof(double),“x”);
公共静态表达式GetExpression(字符串s)
{
ParameterExpression ParameterExpression=Expression.Parameter(typeof(double),“x”);
表达式结果=GetExpressionInternal;
返回表达式.Lambda(结果,参数表达式);
}
私有静态表达式GetExpressionInternal(字符串s)
{
双常数;
如果(s==“x”)
返回参数;
if(双锥巴色(s,输出常数))
返回表达式.Constant(Constant,typeof(double));
foreach(SupportedOps中的字符操作)
{
var split=s.split(新[]{op},StringSplitOptions.RemoveEmptyEntries);
如果(拆分长度>1)
{
var expression=GetExpressionInternal(拆分[0]);
for(int i=1;i
但我有一个错误:
未处理的异常: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
访问或)在。。。等等
请告诉我,这是怎么解决的?这里我有一个全局参数并引用它,所以我不知道为什么它会这样说。代码的问题是,您有两个
x
参数的实例。其中一个是跨表达式生成使用的私有静态字段,第二个是在Lambda创建中创建和使用的字段
如果您有ParameterExpression,那么您应该在表达式中使用相同的实例,并将相同的实例传递到Lambda生成中,否则它将像您的示例中那样失败
如果您删除参数expression
,并使用私有参数
字段,则可以正常工作,如下所示:
public static Expression<Func<double, double>> GetExpression(string s)
{
Expression result = GetExpressionInternal(s);
return Expression.Lambda<Func<double, double>>(result, Parameter);
}
公共静态表达式GetExpression(字符串s)
{
表达式结果=GetExpressionInternal;
返回表达式.Lambda(结果,参数);
}
在.NetFiddle中的工作示例-该死的复制粘贴,我没有引用任何对象。这显然是这种行为的原因。谢谢你help@AlexZhukovskiy:我正在使用您的代码,它使用简单的公式非常有效,但如何扩展代码以支持括号:
()
和e
Math.Expr
。我试过了,但只得到了一点exceptions@PaulMeems要使它与括号一起工作并不容易,因为它们正在改变优先级,而我只是用string.Split进行除法,它忽略了它。你基本上必须从头重写它。你可以先尝试打开方括号(从3*(x+2)转换到3*x+3*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;
}