为带有移位参数的不同函数编写模板
Writing a template for varying functions with shifted arguments
我希望这个问题(及其可能的答案)在范围上足够普遍,以便对其他人也有用。
我正在尝试求解涉及 double
个函数 的乘积的数值问题,形式为
其中 是预定义函数,我将 传递给集成商。
复杂的是我的函数 不是静态的;积分是重复进行的,每次积分时 都有不同的形式,例如,在第一个循环中它可能是
秒变成
等等,其中 表示 函数向量的成员。在积分的每次迭代中,我先验地不知道 的形式是什么,只知道它将是 s 与常数系数的线性组合。
由于我传递给集成器的 涉及带有 "shifted" 参数的 ,因此在每次迭代时我都需要访问每个 ,因为我需要调用形式
double Y (double x, double y, double z){
return (f(x+y) - f(x)) * (f(y+z) - f(y)) * A(z+y);
}
对于集成商(或者可能不是?)。
我的问题是,代表我的 的最佳方式是什么?我一直在阅读明显的候选者(模板,std::function),但我不确定如何 "impose" 将转移的参数转移到函数上。
我敢肯定,在另一个化身中,这是一个相当典型的编程设计问题。谁能给我一些关于如何处理它的建议?
编辑:感谢 Rostislav,尤其是 Gombat,他提出了最初的解决方案(尽管 Rostislav 的非常优雅)。在结束这个问题之前,也许我会在这里追加一个进一步的问题,即考虑(更一般的)情况,其中没有关于输出 f(x)
的详细信息(它是 f_i(x)
具有常数系数)在每次迭代中,而我们只知道 f(x) = F(x)
,其中 F(x)
是一个 double
函数,它再次由 f_i(x)
组成,但是现在以任意方式。所有其他条件都相同(从已知 f_0(x)
开始),在这种情况下我应该如何表示我的 F(x)
?
我在想,既然我们知道解的函数依赖性F(x)
(它只依赖于一个参数x
,是一个double
,并且由已知的f_i(x)
),也许可以写一个 "template"(对滥用命名法表示歉意,它 很有启发性)函数 F(x)
我可以传递给集成器,然后集成器 "unwraps" 要集成的函数的实际形式,尽管这可能很复杂。
根据评论,希望以下内容对您有所帮助:
class MyF
{
public:
// typedef as abbreviation
typedef std::function<double(const double)> func;
MyF( const std::vector<func>& functions )
: m_functions(functions)
#ifdef _DEBUG
, m_lcUpdated(false)
#endif
{ }
// f combines the inner functions depending on m_linearCombination
double operator()(const double x)
{
assert( m_lcUpdated );
assert( m_linearCombination.size() > 0 );
assert( m_linearCombination.size() <= m_functions.size() );
double ret(0.0);
// if m_linearCombination has a value only for the first function
// no need to run the loop for all functions, so we iterate only
// over the linearCombination entries.
for ( size_t i = 0; i < m_linearCombination.size(); i++ )
{
// if the factor is very small, no need for m_function call
if ( std::abs(m_linearCombination) < DBL_EPSILON ) continue;
ret += m_linearCombination[i] * m_functions[i](x);
}
m_lcUpdated = false;
return ret;
}
void setLinearCombination(const std::vector<double>& lc)
{
m_linearCombination = lc;
#ifdef _DEBUG
m_lcUpdated = true;
#endif
}
private:
std::vector<double> m_linearCombination;
std::vector<func> m_functions;
#ifdef _DEBUG
bool m_lcUpdated;
#endif
};
MyF
的构造函数在第一次迭代中(或之前?您也可以在没有参数的情况下构造 MyF
之前获取函数 f_i(x)
并编写一个设置方法来设置 vector
共 std::function
秒)。
私有成员 m_lcUpdated
检查在使用 operator()
.
调用函数之前是否更新了线性组合
std::vector<MyF::func> functions;
// ...fill the functions
// construct your function class
MyF F(functions);
// and the variable to fill in each iteration on how to linear combine the inner functions
std::vector<double> linearCombination;
// Provide the first linear combination (fill it before of course)
F.setLinearCombination(linearCombination);
// In each iteration, call the method combining inner functions
F(x); // it works like this, since it is the operator() which is overloaded for const double as input
// And provide the linear combination for the next iteration
F.setLinearCombination( linearCombination );
也可以将 linearCombinations 作为 std::map<size_t,double>
,其中 first 是索引,这样您就不必遍历其值可能为零的元素,如果线性组合是可能的组合是例如f_0(x)
+ f_2(x)
.
编辑:我实现了@Gombat 的解决方案,因为它更直观,但肯定还会看到下面 Rostislav 的回答。
鉴于你是在集成Y
,在集成过程中很可能会调用很多次,所以最好避免使用std::function
,因为编译器基本上无法内联对std::function
由于 std::function
内部使用的类型擦除。
相反,您可以直接使用 lambda。如果您可以控制集成过程的实现(或者它已经将被集成的函数类型作为模板参数),那么您可以避免所有 std::function
用法。否则,仅将 std::function
用于与集成过程的通信是有意义的,但应尽可能避免使用它们。下面是实现它的示例代码(实例here):
#include <iostream>
#include <string>
#include <tuple>
#include <functional>
template<typename... Fn>
class LinearCombination {
public:
template<typename... Un>
LinearCombination(Un&&... fs)
: functions(std::forward<Un>(fs)...)
{
coefs.fill(0.0);
}
double operator()(double x) const
{
return evaluateImpl(x, std::integral_constant<size_t, sizeof...(Fn)-1>());
}
void setCoef(size_t i, double c)
{
coefs[i] = c;
}
private:
template<size_t I>
double evaluateImpl(double x, std::integral_constant<size_t, I> index) const
{
return evaluateOne(x, index) + evaluateImpl<I - 1>(x, std::integral_constant<size_t, I - 1>());
}
template<size_t I>
double evaluateImpl(double x, std::integral_constant<size_t, 0> index) const
{
return evaluateOne(x, index);
}
template<size_t I>
double evaluateOne(double x, std::integral_constant<size_t, I>) const
{
auto coef = coefs[I];
return coef == 0.0 ? 0.0 : coef * std::get<I>(functions)(x);
}
std::tuple<Fn...> functions;
std::array<double, sizeof...(Fn)> coefs;
};
// This helper function is there just to avoid writing something like...
// LinearCombination<decltype(f1), decltype(f2)> when you use lambdas f1 and f2
template<typename... Fn>
auto make_linear_combination(Fn&&... fn)
{
return LinearCombination<Fn...>{std::forward<Fn>(fn)...};
}
double A(double)
{
return 1.0;
}
/// Integration 1.0
double integrate3D(double(*f)(double, double, double))
{
return f(1, 2, 3);
}
struct YHelper {
static double Y(double x, double y, double z)
{
return (f(x + y) - f(x)) * (f(y + z) - f(y)) * A(z + y);
}
static std::function<double(double)> f;
};
std::function<double(double)> YHelper::f;
/// Integration 2.0
template<typename Integrable>
double integrate3D_2(Integrable&& f)
{
return f(1, 2, 3);
}
int main()
{
auto f1 = [](double x) { return x; };
auto f2 = [](double x) { return 2 * x; };
auto lc = make_linear_combination(std::move(f1), std::move(f2));
lc.setCoef(0, 1.0);
lc.setCoef(1, -1.0);
std::cout << lc(2.0) << "\n";
YHelper::f = std::ref(lc);
std::cout << integrate3D(&YHelper::Y) << "\n";
auto Y = [&lc](double x, double y, double z) { return (lc(x + y) - lc(x)) * (lc(y + z) - lc(y)) * A(z + y); };
std::cout << integrate3D_2(Y) << "\n";
}
请注意,在 Integration 1.0
下是您无法控制集成过程签名的情况下的示例实现。 Integration 2.0
下的代码不仅更简洁,而且性能也会更好。
PS:当然,在 evaluateOne
中比较 coef
和 0.0
时要小心 - 假设只有当您将系数直接设置为文字 0.0
而不是任何计算的结果。否则,使用 abs(coef) < epsilon
和适合您的应用程序的 epsilon
值。
我希望这个问题(及其可能的答案)在范围上足够普遍,以便对其他人也有用。
我正在尝试求解涉及 double
个函数
其中
复杂的是我的函数
秒变成
等等,其中
由于我传递给集成器的
double Y (double x, double y, double z){
return (f(x+y) - f(x)) * (f(y+z) - f(y)) * A(z+y);
}
对于集成商(或者可能不是?)。
我的问题是,代表我的
我敢肯定,在另一个化身中,这是一个相当典型的编程设计问题。谁能给我一些关于如何处理它的建议?
编辑:感谢 Rostislav,尤其是 Gombat,他提出了最初的解决方案(尽管 Rostislav 的非常优雅)。在结束这个问题之前,也许我会在这里追加一个进一步的问题,即考虑(更一般的)情况,其中没有关于输出 f(x)
的详细信息(它是 f_i(x)
具有常数系数)在每次迭代中,而我们只知道 f(x) = F(x)
,其中 F(x)
是一个 double
函数,它再次由 f_i(x)
组成,但是现在以任意方式。所有其他条件都相同(从已知 f_0(x)
开始),在这种情况下我应该如何表示我的 F(x)
?
我在想,既然我们知道解的函数依赖性F(x)
(它只依赖于一个参数x
,是一个double
,并且由已知的f_i(x)
),也许可以写一个 "template"(对滥用命名法表示歉意,它 很有启发性)函数 F(x)
我可以传递给集成器,然后集成器 "unwraps" 要集成的函数的实际形式,尽管这可能很复杂。
根据评论,希望以下内容对您有所帮助:
class MyF
{
public:
// typedef as abbreviation
typedef std::function<double(const double)> func;
MyF( const std::vector<func>& functions )
: m_functions(functions)
#ifdef _DEBUG
, m_lcUpdated(false)
#endif
{ }
// f combines the inner functions depending on m_linearCombination
double operator()(const double x)
{
assert( m_lcUpdated );
assert( m_linearCombination.size() > 0 );
assert( m_linearCombination.size() <= m_functions.size() );
double ret(0.0);
// if m_linearCombination has a value only for the first function
// no need to run the loop for all functions, so we iterate only
// over the linearCombination entries.
for ( size_t i = 0; i < m_linearCombination.size(); i++ )
{
// if the factor is very small, no need for m_function call
if ( std::abs(m_linearCombination) < DBL_EPSILON ) continue;
ret += m_linearCombination[i] * m_functions[i](x);
}
m_lcUpdated = false;
return ret;
}
void setLinearCombination(const std::vector<double>& lc)
{
m_linearCombination = lc;
#ifdef _DEBUG
m_lcUpdated = true;
#endif
}
private:
std::vector<double> m_linearCombination;
std::vector<func> m_functions;
#ifdef _DEBUG
bool m_lcUpdated;
#endif
};
MyF
的构造函数在第一次迭代中(或之前?您也可以在没有参数的情况下构造 MyF
之前获取函数 f_i(x)
并编写一个设置方法来设置 vector
共 std::function
秒)。
私有成员 m_lcUpdated
检查在使用 operator()
.
std::vector<MyF::func> functions;
// ...fill the functions
// construct your function class
MyF F(functions);
// and the variable to fill in each iteration on how to linear combine the inner functions
std::vector<double> linearCombination;
// Provide the first linear combination (fill it before of course)
F.setLinearCombination(linearCombination);
// In each iteration, call the method combining inner functions
F(x); // it works like this, since it is the operator() which is overloaded for const double as input
// And provide the linear combination for the next iteration
F.setLinearCombination( linearCombination );
也可以将 linearCombinations 作为 std::map<size_t,double>
,其中 first 是索引,这样您就不必遍历其值可能为零的元素,如果线性组合是可能的组合是例如f_0(x)
+ f_2(x)
.
编辑:我实现了@Gombat 的解决方案,因为它更直观,但肯定还会看到下面 Rostislav 的回答。
鉴于你是在集成Y
,在集成过程中很可能会调用很多次,所以最好避免使用std::function
,因为编译器基本上无法内联对std::function
由于 std::function
内部使用的类型擦除。
相反,您可以直接使用 lambda。如果您可以控制集成过程的实现(或者它已经将被集成的函数类型作为模板参数),那么您可以避免所有 std::function
用法。否则,仅将 std::function
用于与集成过程的通信是有意义的,但应尽可能避免使用它们。下面是实现它的示例代码(实例here):
#include <iostream>
#include <string>
#include <tuple>
#include <functional>
template<typename... Fn>
class LinearCombination {
public:
template<typename... Un>
LinearCombination(Un&&... fs)
: functions(std::forward<Un>(fs)...)
{
coefs.fill(0.0);
}
double operator()(double x) const
{
return evaluateImpl(x, std::integral_constant<size_t, sizeof...(Fn)-1>());
}
void setCoef(size_t i, double c)
{
coefs[i] = c;
}
private:
template<size_t I>
double evaluateImpl(double x, std::integral_constant<size_t, I> index) const
{
return evaluateOne(x, index) + evaluateImpl<I - 1>(x, std::integral_constant<size_t, I - 1>());
}
template<size_t I>
double evaluateImpl(double x, std::integral_constant<size_t, 0> index) const
{
return evaluateOne(x, index);
}
template<size_t I>
double evaluateOne(double x, std::integral_constant<size_t, I>) const
{
auto coef = coefs[I];
return coef == 0.0 ? 0.0 : coef * std::get<I>(functions)(x);
}
std::tuple<Fn...> functions;
std::array<double, sizeof...(Fn)> coefs;
};
// This helper function is there just to avoid writing something like...
// LinearCombination<decltype(f1), decltype(f2)> when you use lambdas f1 and f2
template<typename... Fn>
auto make_linear_combination(Fn&&... fn)
{
return LinearCombination<Fn...>{std::forward<Fn>(fn)...};
}
double A(double)
{
return 1.0;
}
/// Integration 1.0
double integrate3D(double(*f)(double, double, double))
{
return f(1, 2, 3);
}
struct YHelper {
static double Y(double x, double y, double z)
{
return (f(x + y) - f(x)) * (f(y + z) - f(y)) * A(z + y);
}
static std::function<double(double)> f;
};
std::function<double(double)> YHelper::f;
/// Integration 2.0
template<typename Integrable>
double integrate3D_2(Integrable&& f)
{
return f(1, 2, 3);
}
int main()
{
auto f1 = [](double x) { return x; };
auto f2 = [](double x) { return 2 * x; };
auto lc = make_linear_combination(std::move(f1), std::move(f2));
lc.setCoef(0, 1.0);
lc.setCoef(1, -1.0);
std::cout << lc(2.0) << "\n";
YHelper::f = std::ref(lc);
std::cout << integrate3D(&YHelper::Y) << "\n";
auto Y = [&lc](double x, double y, double z) { return (lc(x + y) - lc(x)) * (lc(y + z) - lc(y)) * A(z + y); };
std::cout << integrate3D_2(Y) << "\n";
}
请注意,在 Integration 1.0
下是您无法控制集成过程签名的情况下的示例实现。 Integration 2.0
下的代码不仅更简洁,而且性能也会更好。
PS:当然,在 evaluateOne
中比较 coef
和 0.0
时要小心 - 假设只有当您将系数直接设置为文字 0.0
而不是任何计算的结果。否则,使用 abs(coef) < epsilon
和适合您的应用程序的 epsilon
值。