関数オブジェクト(かんすうオブジェクト、)は、プログラミング言語において、関数(サブルーチンないしプロシージャ)を、オブジェクトとしたものである。手続きオブジェクトとも言う(プロシージャ=手続き)。なお、ここでのオブジェクトの語は、いわゆるオブジェクト指向のそれに限らず、「第一級オブジェクト」という語におけるのと同じ、メモリ上に空間を確保されたもの、といった意味である。関数が第一級オブジェクトである場合は特に第一級関数と言う。関数と変数の名前空間が共通である言語の場合、構文の設計によっては、codice_1 といったような、通常のサブルーチン呼び出しと全く同じ構文で、関数オブジェクトが意味するサブルーチンを呼び出せる言語もある。また、変数束縛が閉じられた関数オブジェクトはクロージャである。C#などには関数オブジェクトのようなものとして、オブジェクトのインスタンスとそのメソッドである手続きとを結びつけている、デリゲートがある。無名関数も参照。関数オブジェクトの典型的な用途は、より優れたコールバックを記述することである。C言語では、コールバックには関数へのポインタを使う他ないが、コールバックの内外で状態変数を共有できない。この制限のために、関数の動的な振る舞いが制約されてしまう。以下に擬似コードで例を示す。これに対し、関数オブジェクトを使うことでFacade パターンを実装し、こうした問題を解決することができる。LISP や、C++、Java、Perl、Python、Ruby などの現代的なオブジェクト指向言語は、ほとんどが関数オブジェクト、ないし同等の機能をサポートしており、さらに有意義な使い方をしているものもある。関数オブジェクトは、LISPにおいてその初期から研究された。計算機プログラムの構造と解釈の第3章でも解説されている。オブジェクト指向言語では、Smalltalk において、ブロックが関数オブジェクトの記法となるよう設計された。たとえば配列の各要素を通常の大小関係とは違う順序で並べ替えたい場合、比較のための関数オブジェクトを引数に取るソートメソッドを、引数としてブロックを付けて、呼び出す。ブロック内には、カスタマイズ版の比較手続きを記述する。ソートメソッド内での比較は、渡された関数オブジェクトの手続きを呼び出すことで行われ、期待する大小関係でのソートがおこなわれる。これは、strategy デザインパターンの完全な具現化であり、プラガブルな振る舞いを促進するものである。二つの要素の順序関係を定義するコールバック関数を用いて並べ替えを行うルーチンの例を考えてみよう。関数へのポインタを使用する C のプログラムは、たとえば下記のようになる:C++ では、オブジェクトのクラスにおいて、operator() メンバー関数を定義すると、関数呼び出し演算子の多重定義により、そのオブジェクトのインスタンスを指す変数の変数名が、あたかも関数名であるかのような構文で、定義した関数を呼ぶことができる。このような C++ のオブジェクトを C++ の用語ではファンクタ(functor)と呼ぶ(#C++のファンクタとファンクショノイド を参照)。コールバックを codice_2 関数に渡す文法は同じだが、関数へのポインタではなくオブジェクトが渡されていることに注意しよう。コールバック関数が実行されると、他のメンバー関数と同様に働き、すなわちオブジェクトの他のメンバー(データや関数)に対して完全にアクセスすることができる。関数オブジェクトはコールバック関数以外の状況でも使用することができる。下記のような例である:クラス型の関数オブジェクトに加えて、C++ では別の種類の関数オブジェクトが可能である。C++ のメンバーポインタや、テンプレート機能を利用することができ、テンプレートの記述力により、(などの)別種の関数オブジェクトを定義するといったいくつかの関数型言語の技法を用いることができる。C++ の Standard Template Library (STL) では、テンプレートによる関数オブジェクトを多用している。以上のように C++ では、関数オブジェクトは、「関数呼び出しと同じ構文で、メンバ関数を呼ぶことができるオブジェクト」として実装されている。これを C++ の用語ではファンクタと呼んでいるが、これはStandard MLのfunctorや、数学における関手とは関係ない(と考えたほうが良い)。C++ では、主要なメソッド一つを持つオブジェクトを「ファンクショノイド」と言い、その一種で、その主要なメソッドが operator() であるオブジェクトが「ファンクタ」である、と説明される。これは C++ の用語であり、C++ を離れた文脈では、関数オブジェクトすなわちファンクタ、ではないので注意。C++ における関数オブジェクトの利点のひとつは関数のポインタと異なり、インライン化できるためパフォーマンスが良い点である。たとえば、引数をインクリメントさせるシンプルな関数は関数オブジェクトとして実装できる:通常の関数:STL 関数 codice_3 を用いると下記のようになる:ここに codice_3 を適用すると下記のようになる:いずれの codice_5も期待通りの動作をするが、最初の方法では以下のように展開される。二番目の方法では以下のように展開される。codice_6の場合には関数が既知であるためコンパイラがインライン化できるが、codice_7 の場合にはコンパイル時に関数が不定でありインライン化できない。現実には、コンパイラに指示されれば簡単に関数を既知にすることができる。コンパイラが関数の定義を認識しており、それがクラスの内外いずれでも同じように行われていさえすればよい。インライン化しない場合、リンカは関数がクラスの関数だとの指示さえあれば同じ関数の別のコンパイル単位の複数回の定義をエラーを生成せずに黙って見過ごす。リンカは同じ関数の定義がクラスの関数でない場合には複数回の定義を許容しないためである。関数オブジェクトの利点の一つは、関数の呼び出しをまたいで状態を(オブジェクトのフィールドとして)保持できる点である。たとえば、下記のコードは10以上の数を数えるジェネレータ(引数をとらない関数)を定義し、11 回呼び出し結果を出力している。D言語には関数オブジェクトとして、デリゲートとクロージャの両方がある。D言語におけるデリゲートとクロージャの違いは、コントロールが変数のスコープから一旦抜けても、変数の寿命が続いているか、そうでないかである。コンパイラにより保守的に、自動的に決定される(後から変数を参照する可能性があればクロージャとする)。D言語は、関数リテラルやラムダ式もサポートしている。コンパイラがインライン化できるようにするため(上記参照)、関数オブジェクトをC++形式の演算子のオーバーロードを用いて宣言することもできる。しかし、D言語ではテンプレート引数として関数等を渡す手法が一般的である。Javaでは関数が第一級オブジェクトでないため、関数オブジェクトの代わりとして、型としては一つのメソッドを持つインタフェースが使われる。代表的なインタフェースとしては、codice_8 が挙げられる。コーディングにおいては、そのようなインタフェースを実装した無名がしばしば使われる。Java の標準ライブラリの例では、java.util.Collections.sort() はリストと、意味的には関数を引数にとる。この関数はリスト内のオブジェクトを比較する役割を持つ。しかし、Java は関数が第一級オブジェクトでないため、Comparator インタフェースを実装したオブジェクトを渡す。下記の例のように使用する(無名インナークラスを使っている):Python では関数は、文字列や数値、リストなどをはじめとする他の任意のデータと同様のオブジェクトであり、第一級関数を扱うことができる言語である。また、codice_9 メソッドを持つ任意のオブジェクトを関数呼び出しの構文で呼び出すことができる。例として、Accumulator クラス(ポール・グレアムのプログラミング言語の文法と明快さの研究に登場する) を挙げる。下記のように使用する(対話的インタプリタを用いている):codice_10Python で関数オブジェクトを定義するもう一つの方法として、ネストした関数定義、といった形の構文を使う方法がある。一方で Python には、その内部に式しか書くことができない(文が書けない)という強い制限のある lambda form しか関数リテラルに相当するものがなく、手続き的なものは名前を付けて定義しなければならない。これはそうするのが良いプラクティスだからとされている。Lisp においても、関数は、文字列やベクトル・リスト・数値と同様に変数に入れたり関数から返したりできる第一級オブジェクトであり、第一級関数を扱うことができる言語である。Lisp はその最初から codice_11 という特殊形式による関数リテラルをはじめとして、第一級関数を扱うことができる言語であったが、1960年代から1970年前後までの実装では、動的スコープのためクロージャにはなっておらず、funarg問題()が認識されることとなった。Scheme で、静的スコープと無限エクステント(メモリ上のオブジェクトは参照されている限り生存し続ける)による解決が示され、Common Lisp をはじめとする現代的な Lisp の多くは静的スコープを採用しており、lambda 特殊形式ではクロージャが作られる。しかし Emacs Lisp のように動的スコープの Lisp もまだ広く残っている。Scheme では変数と関数で名前空間が分かれておらず、変数名を関数名と同様に使ってプログラムを書くことができる。これを実行すると、のように出力される。これに対し、伝統的な Lisp の多くや Common Lisp は変数と関数で名前空間が分かれている。同様のプログラムを Common Lisp で書いた例を示す。これを実行すると、のように出力される。Common Lisp では、名前に対して、変数としての値と、関数とが別々に結びつけられていて、文脈により(カッコ内の並びの先頭にあるか、そうでないかにより)どちらかがアクセスされる。F という変数に関数値を束縛する所では、通常の文脈において関数にアクセスするために codice_12 を名前の前に付けている。一方、並びの先頭の位置に、この例では F と書いても、その名前の関数がないというエラーになる。Common Lisp では一般に、FUNCALL か APPLY 関数を使って呼び出す。変数と関数の名前空間を分けるか同じにするか、という議論は、Lisp に限らず現代的なプログラミング言語の設計において話題になる。ML などの現代的な関数型言語では当然のように同じ名前空間である。スクリプティング言語では、Python や JavaScript は同じ名前空間としたが、Ruby ではローカル変数とメソッドで別の名前空間とした。Lisp では「Lisp-1 対 Lisp-2 の議論」などと呼ばれる( を参照)。Ruby では、便宜上 Object クラスのローカルインスタンスメソッドを「グローバル関数」と呼んでいるといった例外はあるが、関数は存在せず、全てメソッドである。メソッドはオブジェクトではなく、変数とメソッドで名前空間が違う。Ruby には、メソッドの他に手続きの表現としてブロックがある。Ruby のブロックは、メソッド呼び出しにオプショナルに付加できるもので、暗黙の引数といったような感じで呼び出されるメソッドに渡される。ブロックからクロージャが作られ、呼び出された側からは yield という特殊なグローバル関数により、そのクロージャを呼ぶことができる。ブロックは直接にはオブジェクトではない。しかし、メソッド定義の仮引数の記述の最後に、codice_13 のように & を先頭に付けた引数を付けるなどすることで、簡単に Proc オブジェクトとして得ることができる。また、メソッド呼び出しの最後の実引数として、引数の前に & を付けることで、Proc オブジェクトをブロックの代わりに渡すこともできる。Proc オブジェクトの手続きは call というインスタンスメソッドにより呼ぶことができる。Proc オブジェクトは文脈という環境を持つ関数オブジェクト状のものである。これに対し、レシーバ( codice_14 のようにメソッドを呼び出す時、foo の指すオブジェクトをレシーバと言う)という環境を持つ関数オブジェクト状のものが Method オブジェクトである。Method オブジェクトはメソッドそのものではなく、リフレクションなどのためのオブジェクトであり、感じとしては codice_15 に似ている。UnboundMethod はレシーバが切り離された Method であり、実行するためにはまずレシーバを bind して Method にしなければならない。Ruby Extensions Project は、シンプルなハックを開発した。Symbol にこのような to_proc メソッドがあれば、codice_16 メソッドを呼び出すような Proc オブジェクトが、codice_17 というコードで得られる。& が付けられた実引数(前述)が、Proc オブジェクトでない場合は、to_proc メソッドが呼ばれるというコントラクトになっているので(ダックタイピング。これは前からそうなっていた)、たとえば配列の要素の合計を得る、というコードが codice_18 のように簡潔に書ける。Symbol#to_proc は、RubyKaigi 2006 の期間中、2006年6月11日に正式に Ruby に追加された。また、Ruby においてファンクタという名前があるものとして、Ruby Facets プロジェクトによって導入された委譲の実装がある。委譲の最も基本的な定義は下記のようなものである:ここで説明するのは C++ 用語のファンクタではない。より形式化したものとして、数学(圏論)における関手(ファンクタ)と同様のものを持つ言語もある。たとえば、Standard MLのfunctorは、モジュールからモジュールへのマッピング である。HaskellのFunctorは以下のような型クラスである。Prologでは、をファンクタと呼んでいる。
出典:wikipedia
LINEスタンプ制作に興味がある場合は、
下記よりスタンプファクトリーのホームページをご覧ください。