Up

関数オブジェクト(ファンクター)とテンプレート

簡単なサンプルプログラム

 

関数がパラメータをもつ場合、関数オブジェクトとして実装すると扱いやすい。以下に簡単な例を示す。

ロジスティック関数

は、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.250.50.75になるときの引数の値Q1MedQ3を求めるプログラムを以下のように用意してみた。

 

#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++プログラミング入門勁草書房>で説明している。

 

 

Up