C
C言語で関数をカリー化してみる
関数をカリー化するには、その言語が第一級関数をサポートしている必要がある。
第一級関数とは、関数が第一級オブジェクトであるということを言う。
第一級オブジェクトを平たく言うと、動的に生成できて、それが変数に格納できたり、関数の引数や戻り値にできたりするモノだ。(プリミティブは含まない)
つまり、関数を変数に格納したり、他の関数の引数にできたり、関数を戻り値にできれば、関数をカリー化できるよ、いうことになる。
Cは関数ポインタがあるのでこのほとんどができるのだが、関数が第一級オブジェクトであるかというと「違う」らしい。
ちなみに現代のC++はラムダ式(関数オブジェクト)があるので、第一級関数をサポートしている言語だ。
Cで変数に代入できたり関数の引数や戻り値にできるのは「関数ポインタ」つまりアドレス値なのだが、C++のラムダ式もポインタ(正確にはスマートポインタ)としてやりとりできる。
この点ではCの関数もC++のラムダ式も同じだ。
しかしCのポインタが指し示す先はただの関数なので、第一級関数とは言えない。
Cの関数は静的(コンパイル時)にしか生成できず、動的に(プログラム実行中に)生成することができないため、第一級オブジェクトとは呼べないのだ。惜しい感じがする。(そのためかCの関数は「第二級オブジェクト」と呼ばれることがあるようだ)
ということでCの関数はカリー化することができない・・ということになるのだが、Cには構造体がある。
構造体のインスタンスはオブジェクトだ。
ということは構造体内に関数を収めてしまえばカリー化ができそうな気がする。
キャプチャしたい変数があれば、それも構造体に収めることができる。
ここでは引数を2つ取り、その和を返す下のような単純な関数をカリー化してみることにする。
関数そのものを構造体に入れることはできないので、関数ポインタを構造体メンバにし、そこへメンバ関数となる関数へのポインタを代入する。
こうするとなんとなく第一級関数(関数オブジェクト)っぽい形になる。
あとはそのメンバに引数を収めて(つまりキャプチャ)、そのインスタンスを返す関数を作ればカリー化のようなことができる。
一応、カリー化した関数は「add_n_func」ということになるが、「Curry」構造体とそのメンバの「x」、さらにメンバ関数の「add_func」など複数の要素から構成されているので、これを「カリー化」とみなすか「ただ部分適用しただけじゃん」とみなすかは、その人の見解によりそうだ。
|