Вы хорошо начали, и код определенно прояснил некоторые вещи.
Я собираюсь построить это поэтапно.Вы хотите построить выражение наружу, чтобы не потеряться в середине.
Сначала вы хотите добавить x0
и eps
.У вас уже был параметр x0
и константа эпсилон.Я переименовываю некоторые вещи, поэтому я покажу их так, как они есть у меня.
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);