関数オブジェクト(ファンクター)とテンプレート
簡単なサンプルプログラム
関数がパラメータをもつ場合、関数オブジェクトとして実装すると扱いやすい。以下に簡単な例を示す。
ロジスティック関数
は、2つのパラメータとをもつ。これを関数オブジェクト(ファンクター;function object (functor))として実装するが、関数オブジェクトはクラス型のメンバー関数として用意する。この関数オブジェクトとして使用するメンバー関数は演算子「( )」のオーバーライド(override)として宣言される。例えば、上のロジスティック関数の場合、
class
CLogistic {
public:
double
a, s;
double
operator()(double x){
return
1.0 / (1.0 + exp(-(x - a) / s));
}
};
と宣言すればよい。オーバーライドされた演算子「( )」は仮引数リスト「(double 1.0)」をもつので、
CLogistic
f;
f(1.0);
とfを関数名のように呼び出せば、「1.0」を引数の値としてoperator()の関数本体
{
return
1.0 / (1.0 + exp(-(x - a) / s));
}
が実行される。
関数オブジェクトを実引数として他の関数内で利用するときは、テンプレートを用意すると便利である。例えば、関数の根をbisection法で求める場合、以下のようにすればよい。
template
<class TF> void TBisection(TF & f, //
double(*f)(double),
double s, // fが増加関数のとき s
> 0.0
double
c, // 根に対する関数値
double
L_b, double U_b, //
根の存在区間の下限と上限
double
& Root, // 根 f(Root) = c
double
acc)
{
if
((f(L_b) - c) * (f(U_b) -
c) > 0.0){
throw new exception("上限と下限の組み合わせが不適切です。");
}
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);
}
このテンプレートを用いて、ロジスティック関数の値が0.25、0.5、0.75になるときの引数の値Q1、Med、Q3を求めるプログラムを以下のように用意してみた。
#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, //
double(*f)(double),
double s, // fが増加関数のとき s > 0.0
double
c, // 根に対する関数値
double
L_b, double U_b, //
根の存在区間の下限と上限
double
& Root, // 根 f(Root) = c
double
acc)
{
if
((f(L_b) - c) * (f(U_b) -
c) > 0.0){
throw new exception("上限と下限の組み合わせが不適切です。");
}
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;
cout << endl <<
"文字キーを押してから、Enterキーを押して終了。" << endl;
char ck;
cin >> ck;
return
0;
}
上のプログラムの実行例を図1に示す。
図1
プログラムを圧縮ファイルSampleFunctor.zipとしてまとめたので、クリックしてダウンロード・解凍すればVisual Studio Express 2013 for Windows Desktopで開くことができる。
テンプレートおよびオーバーライドについては、<岡本安晴「大学生のための心理学VC++プログラミング入門」勁草書房>で説明している。