假设您在这样的输入中具有lambda函数
Function<double, double> f = x => x*x +2
您想计算点x0的导数。结果方法的签名将为:
Expression<Function<double, double>> GetDerivative(Expression<Function<double, double>> f)
因此,使用此方法将获得一个新的表达式,将其编译并将x0用作参数即可获得结果。 公式是
df(x0) = (f(x0 + eps) - f(x0)) /eps
我现在拥有的是:
public static Expression<Func<double, double>> GetDerivative(Expression<Func<double, double>> func)
{
var eps = 1e-5;
var paramX = Expression.Parameter(typeof(double), "x");
var epsilon = Expression.Constant(eps);
var secondExpression = Expression.Lambda(func, paramX);
//var firstExpression = ..
var expression = Expression.Divide(Expression.Subtract(firstExpression, secondExpression), epsilon);
return Expression.Lambda<Func<double, double>>(expression, paramX);
}
如何使用参数(paramX + epsilon)创建firstExpression?
答案 0 :(得分:1)
您有一个好的开始,并且代码肯定清除了一些内容。
我将逐步构建它。您想向外构建表达式,以免在中间迷路。
首先,您要添加x0
和eps
。您已经有x0
参数和epsilon常数。我正在重命名一些内容,以便在显示时显示给他们。
ParameterExpression x0Parameter = Expression.Parameter(typeof(double), "x0");
ConstantExpression epsilonConstant = Expression.Constant(1e-5);
添加它们是一个简单的表达式:
Expression.Add(x0Parameter, epsilonConstant)
现在,您要将其传递给f(即func
)。为此,您需要做一些事情。首先,您需要一个代表。作为表达式,没有要定位的方法,因此您必须对其进行编译。然后,您必须获取类型及其Invoke
方法。您还需要使编译后的函数可用作调用的目标。
Func<double, double> funcInstance = func.Compile();
Type funcType = typeof(Func<double, double>);
System.Reflection.MethodInfo invokeMethod = funcType.GetMethod("Invoke");
ConstantExpression funcConstant = Expression.Constant(funcInstance, typeof(Func<double, double>));
现在,您可以调用它,并合并您已经构建的表达式。
Expression.Call(funcConstant, invokeMethod, Expression.Add(x0Parameter, epsilonConstant))
下一个表达式是从第一个f(x0)
中减去的表达式。当然,这更简单。您可以重用到目前为止定义的大部分内容。
Expression.Call(funcConstant, invokeMethod, x0Parameter)
现在您要减去这两个表达式。
Expression.Subtract(
Expression.Call(funcConstant, invokeMethod, Expression.Add(x0Parameter, epsilonConstant)),
Expression.Call(funcConstant, invokeMethod, x0Parameter)
)
最后,您想将其除以eps
。
Expression.Divide(
Expression.Subtract(
Expression.Call(funcConstant, invokeMethod, Expression.Add(x0Parameter, epsilonConstant)),
Expression.Call(funcConstant, invokeMethod, x0Parameter)
),
epsilonConstant
)
将它们放在一起,看起来像这样:
public static Expression<Func<double, double>> GetDerivative(Expression<Func<double, double>> func)
{
ParameterExpression x0Parameter = Expression.Parameter(typeof(double), "x0");
ConstantExpression epsilonConstant = Expression.Constant(1e-5);
Func<double, double> funcInstance = func.Compile();
Type funcType = typeof(Func<double, double>);
System.Reflection.MethodInfo invokeMethod = funcType.GetMethod("Invoke");
ConstantExpression funcConstant = Expression.Constant(funcInstance, typeof(Func<double, double>));
BinaryExpression body = Expression.Divide(
Expression.Subtract(
Expression.Call(funcConstant, invokeMethod, Expression.Add(x0Parameter, epsilonConstant)),
Expression.Call(funcConstant, invokeMethod, x0Parameter)
),
epsilonConstant
);
return Expression.Lambda<Func<double, double>>(body, x0Parameter);
}
更新:@ckuri指出,您可以使用Expression.Invoke
来调用func
,而无需进行所有反思。
public static Expression<Func<double, double>> GetDerivative(Expression<Func<double, double>> func)
{
ParameterExpression x0Parameter = Expression.Parameter(typeof(double), "x0");
ConstantExpression epsilonConstant = Expression.Constant(1e-5);
BinaryExpression body = Expression.Divide(
Expression.Subtract(
Expression.Invoke(func, Expression.Add(x0Parameter, epsilonConstant)),
Expression.Invoke(func, x0Parameter)
),
epsilonConstant
);
return Expression.Lambda<Func<double, double>>(body, x0Parameter);
}
回到现实世界,定义您的函数,获取派生函数,将派生函数编译为委托,然后调用委托:
Expression<Func<double, double>> f = x => x * x + 2;
Expression<Func<double, double>> df = GetDerivative(f);
Func<double, double> dfFunc = df.Compile();
double result = dfFunc(someInput);