クロージャって何だろう?
クロージャは関数と環境を一緒にしたものである。クロージャは、関数がレキシカル環境から何かを参照するときには暗黙に必ず作られている。
『ANSI Common Lisp』- 6.5 クロージャ より
どんな風に使うのだろう?
アキュムレータを生成する関数、すなわち、数nを取り、「数iを取ってnをiだけ増加させ、その増加した値を返す関数」を返す関数だ(「増加させる」に注意。ただ足すわけではない。アキュムレータ(蓄積器)だから蓄積されなければ)。
Common Lispではこんなふうになるだろう。(defun foo (n) (lambda (i) (incf n i))『ハッカーと画家』- 第13章 付録 より
レキシカル環境を暗黙的でなく明示的に作るのとどう違うのだろうか?
というわけでC言語に翻訳してみた。
/* foo関数のレキシカル環境 */ typedef struct foo_env { int n; } foo_env; /* 数iを取ってnをiだけ増加させ、その増加した値を返す関数 */ int foo_incf(foo_env *env, int i) { env->n += i; return env->n; } /* foo関数が返すクロージャ */ typedef struct foo_closure { foo_env env; int (*lambda)(foo_env *, int); } foo_closure; /* 数nを取り、*/ /*「数iを取ってnをiだけ増加させ、その増加した値を返す関数」*/ /* を返す関数 */ foo_closure foo(int n) { foo_closure c; c.env.n = n; c.lambda = foo_incf; return c; }
使い心地。
#include <stdio.h> int main() { foo_closure c1 = foo(30); foo_closure c2 = foo(20); printf("%d\n", c1.lambda(&c1.env, 1)); /* 31 (30 + 1) */ printf("%d\n", c2.lambda(&c2.env, 3)); /* 23 (20 + 3) */ printf("%d\n", c1.lambda(&c1.env, 5)); /* 36 (31 + 5) */ printf("%d\n", c2.lambda(&c2.env, 7)); /* 30 (23 + 7) */ printf("%d\n", c1.lambda(&c1.env, 11)); /* 47 (36 + 11) */ return 0; }
マクロでうまいことやれば少しは使いやすくなるかも。