関数オブジェクト
[Wikipedia|▼Menu]
.mw-parser-output .ambox{border:1px solid #a2a9b1;border-left:10px solid #36c;background-color:#fbfbfb;box-sizing:border-box}.mw-parser-output .ambox+link+.ambox,.mw-parser-output .ambox+link+style+.ambox,.mw-parser-output .ambox+link+link+.ambox,.mw-parser-output .ambox+.mw-empty-elt+link+.ambox,.mw-parser-output .ambox+.mw-empty-elt+link+style+.ambox,.mw-parser-output .ambox+.mw-empty-elt+link+link+.ambox{margin-top:-1px}html body.mediawiki .mw-parser-output .ambox.mbox-small-left{margin:4px 1em 4px 0;overflow:hidden;width:238px;border-collapse:collapse;font-size:88%;line-height:1.25em}.mw-parser-output .ambox-speedy{border-left:10px solid #b32424;background-color:#fee7e6}.mw-parser-output .ambox-delete{border-left:10px solid #b32424}.mw-parser-output .ambox-content{border-left:10px solid #f28500}.mw-parser-output .ambox-style{border-left:10px solid #fc3}.mw-parser-output .ambox-move{border-left:10px solid #9932cc}.mw-parser-output .ambox-protection{border-left:10px solid #a2a9b1}.mw-parser-output .ambox .mbox-text{border:none;padding:0.25em 0.5em;width:100%;font-size:90%}.mw-parser-output .ambox .mbox-image{border:none;padding:2px 0 2px 0.5em;text-align:center}.mw-parser-output .ambox .mbox-imageright{border:none;padding:2px 0.5em 2px 0;text-align:center}.mw-parser-output .ambox .mbox-empty-cell{border:none;padding:0;width:1px}.mw-parser-output .ambox .mbox-image-div{width:52px}html.client-js body.skin-minerva .mw-parser-output .mbox-text-span{margin-left:23px!important}@media(min-width:720px){.mw-parser-output .ambox{margin:0 10%}}

この記事には複数の問題があります。改善ノートページでの議論にご協力ください。

出典がまったく示されていないか不十分です。内容に関する文献や情報源が必要です。(2024年4月)


脚注による出典や参考文献の参照が不十分です。脚注を追加してください。(2024年4月)


独自研究が含まれているおそれがあります。(2024年4月)
出典検索?: "関数オブジェクト" ? ニュース ・ 書籍 ・ スカラー ・ CiNii ・ J-STAGE ・ NDL ・ dlib.jp ・ ジャパンサーチ ・ TWL

関数オブジェクト(かんすうオブジェクト、: function object)は、プログラミング言語において、関数(サブルーチンまたはプロシージャ)を、オブジェクトとしたものである。@media screen{.mw-parser-output .fix-domain{border-bottom:dashed 1px}}手続きオブジェクトとも言う(プロシージャ=手続き)[要出典]。なお、ここでのオブジェクトの語は、いわゆるオブジェクト指向のそれに限らず、「第一級オブジェクト」という語におけるのと同じ、メモリ上に領域を確保されたもの、といった意味である。関数が第一級オブジェクトである場合は特に第一級関数と言う。

関数と変数の名前空間が共通である言語の場合[要追加記述]、構文の設計によっては、y = f(x) といったような、通常のサブルーチン呼び出しと全く同じ構文で、関数オブジェクトが保持しているサブルーチンを呼び出せる言語もある。一方、通常のサブルーチンのように呼び出すことはできず、applyやacceptやgetといった特別な名前のメソッドを経由して呼び出す必要のある言語もある。

また、変数束縛が閉じられた関数オブジェクトはクロージャである。C#などの.NET言語には関数オブジェクトのようなものとして、オブジェクトのインスタンスとその振る舞いであるメソッドとを結びつけて管理することのできる、デリゲートがある。無名関数も参照。
用途

関数オブジェクトの典型的な用途は、より優れたコールバックを記述することである。C言語では、コールバックには関数へのポインタを使う他ないが、コールバックの内外で状態変数を共有できない。この制限のために、関数の動的な振る舞いやインターフェイス設計が制約されてしまう。以下に例を示す。/* ライブラリ側のコード */typedef void my_callback_func_t(void);typedef struct my_handler { my_callback_func_t* callback;} my_handler_t;void register_callback(my_handler_t* handler, my_callback_func_t* func) { handler->callback = func;}void invoke_callback(my_handler_t* handler) { if (handler->callback) { handler->callback(); }}void perform_process(my_handler_t* handler) { /* 前処理 */ /* ... */ /* コールバック関数の呼び出し */ invoke_callback(handler); /* 後処理 */ /* ... */}/* アプリケーション側のコード */void callback_func(void) { /* ... */ /* ここで後述の状態変数 state にアクセスしたくても、不可能 */}int main(void) { /* 状態変数 */ static int state = 0; my_handler_t handler = { NULL }; /* コールバックとして callback_func という関数へのポインタを登録 */ register_callback(&handler, &callback_func); perform_process(&handler);}

コールバック関数内で外部の状態変数を参照するためには、グローバル変数を利用したり、構造体メンバーに状態変数へのポインタを持たせたうえでコールバック関数の引数を経由して状態変数のアドレスを渡したり、といった工夫が必要になる。

関数オブジェクトは、上記のように煩雑になりがちな設計パターンを言語機能として組み込み、関数を可搬性の高いオブジェクトとして容易に利用できるようにするものである。

LISPや、SmalltalkC++JavaC#PerlPythonRubyなどの現代的なオブジェクト指向言語は、ほとんどが関数オブジェクト、あるいは同等の機能をサポートしており、さらに有意義な使い方をしているものもある[要追加記述]。
起源

関数オブジェクトは、LISPにおいてその初期から研究された。計算機プログラムの構造と解釈の第3章でも解説されている。

オブジェクト指向言語では、Smalltalk において、ブロックが関数オブジェクトの記法となるよう設計された。たとえば配列の各要素を通常の大小関係とは違う順序で並べ替えたい場合、比較のための関数オブジェクトを引数に取るソートメソッドを、引数としてブロックを付けて、呼び出す。ブロック内には、カスタマイズ版の比較手続きを記述する。ソートメソッド内での比較は、渡された関数オブジェクトの手続きを呼び出すことで行なわれ、期待する大小関係でのソートが行なわれる。これは、Strategyデザインパターンの完全な具現化であり、着脱可能 (pluggable) な振る舞いを促進するものである。
C++ での関数オブジェクト

C++では、クラスあるいは構造体において関数呼び出し演算子多重定義が可能となっている。具体的には、任意の引数を持つoperator()メンバー関数を定義することで、そのオブジェクトのインスタンスを指す変数の名前が、あたかも関数名(関数指示子)であるかのような構文で、定義した関数を呼ぶことができる。このような C++ のオブジェクトを、C++ の用語ではファンクタ (functor) と呼ぶ(#C++のファンクタとファンクショノイド を参照)。struct my_adder { int operator()(int a, int b) const { return a + b; }};my_adder f;int result = f(2, 3);// 以下のように呼び出すことも可能だが、通例使われない。f.operator()(2, 3);

C++のclass/structは、デフォルトのアクセスレベル(アクセス修飾子(英語版))がprivate/publicであるという違いしかないため、関数オブジェクトの記述ではclassの代わりにstructが使われることも多い。

配列の中から特定の条件に該当する要素の個数を数えるルーチンの例を考えてみよう。ただし条件の判定処理は自由にカスタマイズできるものとする。

関数へのポインタを使用する C のプログラムは、たとえば下記のようになる。#include <stdio.h>/* 引数が条件に該当する場合は 1 を、該当しない場合は 0 を返す */typedef int PredicateFunc(int x);size_t countIf(const int data[], size_t num, PredicateFunc* predicate) { size_t count = 0; size_t i; for (i = 0; i < num; ++i) { if (predicate(data[i])) { count++; } } return count;}int isPositive(int x) { return x > 0;}int isEven(int x) { return x % 2 == 0;}int main(void) { const int data[] = { 0, 1, 2, 3, 4, 5, 6, 0, -1, -2, -3, -4 }; printf("Count of positive numbers = %ld\n", (long)countIf(data, sizeof(data) / sizeof(*data), &isPositive)); printf("Count of even numbers = %ld\n", (long)countIf(data, sizeof(data) / sizeof(*data), &isEven));}

一方、C++の関数オブジェクトを利用すると下記のようになる。#include <cstdio>template<typename TPredicate> size_t countIf(const int data[], size_t num, TPredicate predicate) { size_t count = 0; for (size_t i = 0; i < num; ++i) { if (predicate(data[i])) { count++; } } return count;}struct IsPositiveFunctor { bool operator()(int x) const { return x > 0; }};struct IsEvenFunctor { bool operator()(int x) const { return x % 2 == 0; }};int main() { const int data[] = { 0, 1, 2, 3, 4, 5, 6, 0, -1, -2, -3, -4 }; std::printf("Count of positive numbers = %ld\n", (long)countIf(data, sizeof(data) / sizeof(*data), IsPositiveFunctor())); std::printf("Count of even numbers = %ld\n", (long)countIf(data, sizeof(data) / sizeof(*data), IsEvenFunctor()));}

コールバックを述語 (predicate) として countIf() 関数テンプレートに渡す際、関数へのポインタではなく構造体のインスタンス(関数オブジェクト)を渡していることに注意されたい。

上記の例において関数オブジェクトによるカスタマイズを可能にしているのは、テンプレートと関数呼び出し演算子のオーバーロードによる静的ダック・タイピングである。関数テンプレートを実体化する際の字句解析に適合しさえすれば、述語にはどんなオブジェクトでも渡せる。

コールバック関数が実行されると、他のメンバー関数と同様に働き、すなわちオブジェクトの他のメンバー(データや関数)に対して完全にアクセスすることができる。

クラス型の関数オブジェクトに加えて、C++ では別の種類の関数オブジェクトが可能である。[要校閲]

C++ のメンバーポインタや、テンプレート機能を利用することができ、テンプレートの記述力により、(関数の合成などの)別種の関数オブジェクトを定義するといったいくつかの関数型言語の技法を用いることができる。[要校閲]

C++ の Standard Template Library (STL) では、テンプレートの述語 (predicate) として関数オブジェクトを多用している。
C++のファンクタとファンクショノイド


次ページ
記事の検索
おまかせリスト
▼オプションを表示
ブックマーク登録
mixiチェック!
Twitterに投稿
オプション/リンク一覧
話題のニュース
列車運行情報
暇つぶしWikipedia

Size:38 KB
出典: フリー百科事典『ウィキペディア(Wikipedia)
担当:undef