以下のコードが『やさしい Emacs-Lisp 講座』p.21 にある。
まず、C の場合が紹介されている。(一般的な言語の場合という意味で)
scope.c
#include <stdio.h>
static int x = 5;
void subfunc() {
printf("x is [%d] in subfunc\n", x);
}
int main()
{
static int x = 3;
printf("x is [%d] in main\n", x);
subfunc();
}
これをコンパイルして実行すると、以下のようになる。
$ gcc scope.c -o scope
$ ./scope
x is [3] in main
x is [5] in subfunc
本に書いてあるとおり、x は、それぞれの関数の中でしか参照できない。
しかし、Emacs-Lisp の場合は、以下のように書けば、ローカル変数が
他の関数の中にある変数を支配できてしまう。
(setq x 5)
(defun subfunc ()
(insert (format "x is [%d] in subfunc\n" x)))
(defun main ()
(let ((x 3))
(insert (format "x is [%d] in main\n" x))
(subfunc)))
x is [3] in main
x is [3] in subfunc
ただ、まあ、以下のようにすれば、letの束縛から離れるけれど。
(setq x 5)
(defun subfunc ()
(insert (format "x is [%d] in subfunc\n" x)))
(defun main ()
(let ((x 3))
(insert (format "x is [%d] in main\n" x)))
(subfunc))
x is [3] in main
x is [5] in subfunc
『まったく初めての人のためのISLISP』の「スコープとクロージャ」という
項に以下に記述がある。
MonoLis Ver0.02
> (defun f(x) (g 3))
t
> (defun g(y) (+ x y))
t
> (f 2)
5
このあと、「違和感あるでしょ?」と書かれている。
そして、現在この方式を採用してるのは EmacsLisp くらいのものだ、と書かれている。
たしかに、Emacs-Lisp で、上記のコードは、そのまま動く。
(defun f(x) (g 3))
f
(defun g(y) (+ x y))
g
(f 2)
5
引数として宣言された変数の値は、その関数を飛び越えている。
それに対して、この本では、現在の scheme をはじめとする Lisp は、各関数がそれぞれスコープを持っている。そして、クロージャという仕組みも持っているとして、以下のコードが紹介されている。
(defglobal bar 1)
(defun foo (x)
(flet ((boo (y)
x))
(setq bar boo)))
以下のように実行する。
> bar
1
> (foo 3)
<function>
> bar
<function>
> (funcall bar 0)
3
> (funcall bar 999)
3
(foo 3) と実行すると、boo関数が定義される。そのときに x に 3 が与えられる。
それが boo関数が実行されるときに呼び出される。
boo関数が定義されるとき、関数本体とその環境(この場合は x の値)も保持される。
これを Common Lisp で書いてみた。
(defparameter bar 1)
(defun foo (x)
(let* ((boo (lambda (y) x)))
(setq bar boo)))
他にいい書き方あるんだろうなあ。