9

长期浏览器,第一次在这里提问。我编写了许多脚本来执行各种 1D 数值积分方法并将它们编译到库中。我希望该库在能够集成的方面尽可能灵活。

这里我包含一个示例:一个非常简单的梯形规则示例,其中我将指针传递给要集成的函数。

// Numerically integrate (*f) from a to b
// using the trapezoidal rule.
double trap(double (*f)(double), double a, double b) {
  int N = 10000;
  double step = (b-a)/N;
  double s = 0;
  for (int i=0; i<=N; i++) {
    double xi = a + i*step;
    if (i == 0 || i == N) { s += (*f)(xi); }
    else { s += 2*(*f)(xi); }
  }
  s *= (b-a)/(2*N);
  return s;
}

这对于只接受一个参数的简单函数非常有用。例子:

double a = trap(sin,0,1);

但是,有时我可能想要整合具有更多参数的东西,例如二次多项式。在此示例中,系数将由用户在积分之前定义。示例代码:

// arbitrary quadratic polynomial
double quad(double A, double B, double C, double x) {
  return (A*pow(x,2) + B*x + C);
}

理想情况下,我可以做这样的事情来整合它:

double b = trap(quad(1,2,3),0,1);

但显然这行不通。我通过定义一个将系数作为成员并将感兴趣的函数作为成员函数的类来解决这个问题:

class Model {
  double A,B,C;
public:
  Model() { A = 0; B = 0; C = 0; }
  Model(double x, double y, double z) { A = x; B = y; C = z; }
  double func(double x) { return (A*pow(x,2)+B*x+C); }
};

但是,然后我的集成函数需要更改以将对象而不是函数指针作为输入:

// Numerically integrate model.func from a to b
// using the trapezoidal rule.
double trap(Model poly, double a, double b) {
  int N = 10000;
  double step = (b-a)/N;
  double s = 0;
  for (int i=0; i<=N; i++) {
    double xi = a + i*step;
    if (i == 0 || i == N) { s += poly.func(xi); }
    else { s += 2*poly.func(xi); }
  }
  s *= (b-a)/(2*N);
  return s;
}

这很好用,但生成的库不是很独立,因为它需要在某处定义类模型。此外,理想情况下,模型应该能够从用户更改为用户,因此我不想在头文件中修复它。我尝试使用函数模板和仿函数来让它工作,但它不是很独立,因为模板应该在头文件中定义(除非你想显式实例化,我不这样做)。

所以,总结一下:有什么方法可以让我的集成函数接受具有可变数量输入参数的任意一维函数,同时仍然保持足够的独立性,可以将它们编译成一个独立的库?提前感谢您的建议。

4

2 回答 2

8

您需要的是模板和std::bind()boost::bind()如果您买不起 C++11,则需要模板)。例如,这就是你的trap()函数将变成的样子:

template<typename F>
double trap(F&& f, double a, double b) {
  int N = 10000;
  double step = (b-a)/N;
  double s = 0;
  for (int i=0; i<=N; i++) {
    double xi = a + i*step;
    if (i == 0 || i == N) { s += f(xi); }
//                               ^
    else { s += 2* f(xi); }
//                 ^
  }
  s *= (b-a)/(2*N);
  return s;
}

请注意,我们是从函数指针泛化并允许传入任何类型的可调用对象(例如,包括 C++11 lambda)。因此,调用用户提供的函数的语法不是*f(param)(仅适用于对于函数指针),但只是f(param).

关于灵活性,让我们考虑两个硬编码函数(并假装它们是有意义的):

double foo(double x)
{
    return x * 2;
}

double bar(double x, double y, double z, double t)
{
    return x + y * (z - t);
}

您现在可以直接在输入中提供第一个函数trap(),或者将第二个函数的最后三个参数绑定到某个特定值的结果(您可以自由选择要绑定的参数):

#include <functional>

int main()
{
    trap(foo, 0, 42);
    trap(std::bind(bar, std::placeholders::_1, 42, 1729, 0), 0, 42);
}

当然,您可以使用 lambda 获得更大的灵活性:

#include <functional>
#include <iostream>

int main()
{
    trap(foo, 0, 42);
    trap(std::bind(bar, std::placeholders::_1, 42, 1729, 0), 0, 42);

    int x = 1729; // Or the result of some computation...
    int y = 42; // Or some particular state information...
    trap([&] (double d) -> double
    {
        x += 42 * d; // Or some meaningful computation...
        y = 1; // Or some meaningful operation...
        return x;
    }, 0, 42);

    std::cout << y; // Prints 1
}

您还可以传递您自己的有状态函子 tp trap(),或一些包装在对象中的可调用std::function对象(或者boost::function如果您买不起 C++11)。选择范围相当广泛。

这是一个活生生的例子

于 2013-04-11T21:47:23.673 回答
4

你想做的是让这成为可能

trap( quad, 1, 2, 3, 0, 1 );

在 C++11 中,我们有别名模板和可变参数模板

template< typename... Ts >
using custom_function_t = double (*f) ( double, Ts... );

上面定义了一个custom_function_t带有双变量和可变数量的参数的 a。

所以你的trap功能变成

template< typename... Ts >
double trap( custom_function_t<Ts...> f, Ts... args, double a, double b ) {
    int N = 10000;
    double step = (b-a)/N;
    double s = 0;
    for (int i=0; i<=N; i++) {
        double xi = a + i*step;
        if (i == 0 || i == N) { s += f(xi, args...); }
        else { s += 2*f(xi, args...); }
    }
    s *= (b-a)/(2*N);
    return s;
}

用法:

double foo ( double X ) {
  return X;
}

double quad( double X, double A, double B, double C ) {
  return(A*pow(x,2) + B*x + C);
}

int main() {
  double result_foo  = trap( foo, 0, 1 );
  double result_quad = trap( quad, 1, 2, 3, 0, 1 );  // 1, 2, 3 == A, B, C respectively
}

在 Apple LLVM 4.2 编译器上测试。

于 2013-04-11T23:18:21.453 回答