假设我想写一个通用的数值积分脚本,它接受一个用户定义的函数,并在变量int x
上进行积分。进一步假设该函数可以调用任意数量的数据列表;例如,假设我希望积分器f_integrator
能够集成以下任何一个:
integrand(x)=f(x)
integrand(x)=f(x)*g(x)
integrand(x)=(f(x)+g(x)/h(x))
integrand(x)=pow(f(x),2)*k(x)
然后,我们似乎需要它能够只调用一个定义良好的函数,而不指定函数需要多少个参数(也许我们需要指定其中的一部分,例如函数必须具有int x
(。如果是这样的话,我们该如何实施呢?如果我们应该用另一种方式来做,怎么做?
如果需要传递未知数量的参数,可以为f_integrator
使用可变模板
template<typename F, typename... Args >
double f_integrator(F integrand, std::tuple<Args...> const &bounds){
// integrate
}
为了允许它处理采用任意参数类型的函数(假设积分的类型为double
,如果需要,还可以为返回类型添加另一个模板参数(。模板参数F
扮演函数类型的角色。然后,您可以直接在函数上调用f_integrator
,例如类似lambda的
double integral = f_integrate([](double x){return f(x)*g(x);},
std::make_tuple(1.0,2.0));
请注意,您可以将任何可调用对象传递给f_integrate
,因为可调用对象返回您可以集成的东西。
使用std::tuple
允许以函数体中可处理的方式通过积分区域的边界。当然,如果您考虑非平凡的集成区域,事情会变得更复杂,但您仍然可以传递一个由Args
数组表示的网格,以将集成区域的信息传递给f_integrator
。
积分本身是如何执行的是另一回事,但在一个又一个参数上迭代积分不是很有效,如果你想进行高维积分,蒙特卡洛积分可能是一种很好的方法。
这是一个非常通用的解决方案,只要有一些温和的假设,就有更方便的方法来实现它(对于可变模板来说,编写积分器的主体并不是很简单(。如果被积函数的所有自变量都是相同类型的,我们可以像一样通过边界
template<typename F, typename T>
double f_integrate(F integrand, std::vector<T> const &bounds){
// integrate, much more convenient
}
现在,您提供的示例只需要一个参数,所以如果您能很好地集成一维函数,那么可以简单一点。如果您只传递带有单个参数的函数,则不需要可变模板,只需即可
template<typename F, typename T=double>
double f_integrate(F integrand, T const& lower_bound, T const& upper_bound){
// integrate
}
第二个参数可能甚至没有必要,因为你可能只对实数进行积分(注意,整数上的积分只是一个离散和,所以在这种情况下不需要用数字进行积分(。