Function Object (Functor)
and Template
When a function has some parameters, it is appropriate to use a function object (functor).
For example, a logistic function
has two parameters, a and s. Hence, this function can be prepared as a method, i.e., operator ( ) overrided as follows:
class
CLogistic {
public:
double a, s;
double operator()(double x){
return 1.0 / (1.0 + exp(-(x - a) /
s));
}
};
In this
case, by the following code
CLogistic
f;
f(1.0);
the
body of the overrided operator ( )
{
return 1.0 / (1.0 + exp(-(x - a) /
s));
}
is
called with x = 1.0;
When
using a functor as a parameter in another function,
template is useful.
For
example, if various functors may be used in bisection
algorithm, the template would be as follows:
template
<class TF> void TBisection(TF & f, // Functor(double
x)
double s, //
s > 0 or s < 0 if f is increasing or decreasing, respectively.
double c, //
c = f(Root)
double L_b, double U_b, //
Set so that f(L_b) < c < f(U_b)
double & Root, //
The required value so that f(Root) = c
double acc) //
Accuracy
{
if ((f(L_b) - c) * (f(U_b) - c) > 0.0){
throw "Value
error of L_b or U_b.";
}
do {
double m = 0.5 * (L_b + U_b);
double v = f(m);
if (s * (v - c) > 0.0) {
U_b = m;
}
else {
L_b = m;
}
}
while ((abs(U_b - L_b) >= acc * abs(U_b))
&&
((abs(U_b) + abs(L_b))
>= 1.0e-14)
);
Root
= 0.5 * (L_b + U_b);
}
Using
this template, a program, which calculates the values the 1st, 2nd,
and 3rd quantiles, i.e., Q1, Med, and Q3, respectively, is written
as follows;
#include
<iostream>
#include
<iomanip>
#include
<cmath>
#include
<exception>
using
namespace std;
class CLogistic {
public:
double a, s;
double operator()(double x){
return 1.0 / (1.0 + exp(-(x - a) /
s));
}
};
template
<class TF> void TBisection(TF & f, // Functor(double
x)
double s, //
s > 0 or s < 0 if f is increasing or decreasing, respectively.
double c, //
c = f(Root)
double L_b, double U_b, //
Set so that f(L_b) < c < f(U_b)
double & Root, //
The required value so that f(Root) = c
double acc) //
Accuracy
{
if ((f(L_b) - c) * (f(U_b) - c) > 0.0){
throw "Value
error of L_b or U_b.";
}
do {
double m = 0.5 * (L_b + U_b);
double v = f(m);
if (s * (v - c) > 0.0) {
U_b = m;
}
else {
L_b = m;
}
}
while ((abs(U_b - L_b) >= acc * abs(U_b))
&&
((abs(U_b) + abs(L_b))
>= 1.0e-14)
);
Root
= 0.5 * (L_b + U_b);
}
int main(){
CLogistic f;
cout << "a = ";
cin >> f.a;
cout << "s (> 0) =
";
cin >> f.s;
double U_v = 10.0;
while (f(U_v) <= 0.75) U_v += 10.0;
double L_v = -10.0;
while (f(L_v) >= 0.25) L_v -= 10.0;
double FQ1, FMed, FQ3;
TBisection(f,
1.0, 0.25, L_v, U_v, FQ1,
1.0e-12);
TBisection<CLogistic>(f, 1.0, 0.5, L_v, U_v, FMed, 1.0e-12);
TBisection(f,
1.0, 0.75, L_v, U_v, FQ3,
1.0e-12);
cout << "Q1 = "
<< setprecision(9) << fixed << FQ1
<<
" Med = " << FMed
<<
" Q3 = " <<
FQ3 << endl;
return 0;
}
If you run this program, you would get a picture like Figure 1.
Figure 1
The program is packed as main.cpp.zip, which can be downloaded and extended to open by a editor.