01: プログラミング言語とは何か 02: 0から99まで印字するプログラムの解説 03: Pythonで書くと... 04: 変数と関数 05: 条件分岐とループ 06: プリプロセス 07: ポインタと配列1 08: ポインタと配列2 09: ポインタと配列3 0A: 文字列 0B: 多次元配列 0C: ファイル入出力 0D: 構造体 0E: コマンドライン引数 0F: 数値を読み込んで総和と平均を表示するプログラム 10: 数値を読み込んで総和と平均を表示するPythonプログラム 11:【番外編】文章中の単語を出現頻度順に表示するプログラム 12:【番外編】コマンドライン引数を処理するイディオム 13:【番外編】C言語でクロージャを実装する
簡単なクロージャの例として,引数にnを足す関数を生成する関数をC言語で考える.
まずはお約束の#includeから.
#include <stdio.h>
#include <stdlib.h>
やりたいことはSchemeで言えば
(define (make-plus-n n) (lambda (x) (+ n x)))
struct make_plus_n_context_t {
int _n;
int (*_func)(const struct make_plus_n_context_t *, int);
};
typedef struct make_plus_n_context_t MAKE_PLUS_N_CONTEXT_T;
足し算関数の実体を用意しておく.
static int plus_n(const MAKE_PLUS_N_CONTEXT_T *context, int x) {
return context->_n + x;
}
最後に関数make_plus_nを定義する.
MAKE_PLUS_N_CONTEXT_T *make_plus_n(int n) {
MAKE_PLUS_N_CONTEXT_T *context;
context = (MAKE_PLUS_N_CONTEXT_T *)malloc(sizeof(MAKE_PLUS_N_CONTEXT_T));
context->_n = n;
context->_func = plus_n;
return context;
}
この関数は,関数を返す代わりにmake_plus_n_context_t構造体を(メモリを新たに確保して)返す.この構造体から_funcを呼んでやるのは,次のようなマクロを用意すると便利である.
#define FUNC_CALL(context, param) ((context)->_func((context), (param)))
次のように使う.
int main(void) {
MAKE_PLUS_N_CONTEXT_T *plus_2 = make_plus_n(2);
int y = FUNC_CALL(plus_2, 1);
printf("%d\n", y);
free(plus_2);
return 0;
}
Cウィザードはこのようなことは朝飯前にやってしまう.(いや,もっといいコードを書くだろう.)
僕は上記のようなコードを一瞬で書くことはできないので,Cウィザードではないのだろう.ただし,C++版の次のコードならすらすらと出てくる.(標準テンプレートライブラリがstd::plusを用意してくれたおかげであるが.)
template <typename T> std::binder1st< std::plus<T> > make_plus_n(T n) {
return std::binder1st< std::plus<T> >(std::plus<T>(), n);
}
std::binder1st< std::plus<int> > plus_2 = make_plus_n(2);
std::cout << plus_2(3) << std::endl;
Scheme版の
(define (make-plus-n n) (lambda (x) (+ n x)))
ならもっとたやすく書ける.