Hatena::ブログ(Diary)

あどけない話

2008-06-03

Emacs Lisp の構造化

忌み嫌われているグローバル変数/関数をなるべく使用しないために、もう一つ小さな空間を用意している言語があります。僕のイメージでは、この空間はゆりかごです。ゆりかごの実現方法として、以下のようなものが挙げられるでしょう。

Emacs LispLisp なんですから、ある関数をゆりかごにして、中に関数を定義したいところです。でも、これはできません。さて、どうするかという話です。

defun で defun

defun の中で defun したいところですが、これはうまく行きません。外側の関数が実行されると、内側の関数がグローバル空間に定義されてしまうからです。

(defun foo (n)
  (defun bar (n)
    (1+ n))
  (bar n))

(fboundp 'bar)nil
(foo 2)3
(fboundp 'bar)t

ローカル変数

ローカル変数関数を定義するとどうなるでしょうか? こんな感じです。

(defun foo (n)
  (let ((bar (lambda (n) (1+ n))))
    (bar n)))
(foo 2) → エラー

これは実行時にエラーになります。なぜなら、Emacs Lisp では変数名と関数名の空間は分かれているからです。

funcall

では、funcall を使うとどうなるでしょうか? こんな感じです。

(defun foo (n)
  (let ((bar (lambda (n) (1+ n))))
    (funcall 'bar n)))
(foo 2) → エラー

これも実行時にエラーになります。なぜなら、funcall の第一引数のシンボルは、グローバルの関数名の空間を意味するからです。

最後の手

さて困りました。関数の中に関数は定義できそうにありません。しかし、もう一つだけ手が残されています。funcall の第一引数は、シンボルではなく、lambda 式そのものでもいいのです。

つまり、

(defun bar (n) (1+ n))
(funcall 'bar 2)3

と書く代わりに、

(funcall (lambda (n) (1+ n)) 2)3

と書けます。

という訳で、先ほどの 'bar のクオートを外すだけで、ウソのように動いてしまいます。

(defun foo (n)
  (let ((bar (lambda (n) (1+ n))))
    (funcall bar n)))
(foo 2)3
(fboundp 'bar)nil

所感

Emacs Lisp でも、関数の中に関数を定義することが模倣できるとは、ほどんど誰も知りません。なので、このテクニックを使ったコードを見かけたことはありません。(Mew では使っています。)

ytakenakaytakenaka 2008/06/03 20:12 これはねぇ。fletをつかうんですわ。*scratch*で書いた例だけど
(defun test (x)
(flet ((test2 (y)
(+ x y)))
(test2 3)))
test
(test 3)
6
あるいはfletのかわりにlabelsやmacroletでもつくれます。違いはあるけど詳しいことは忘れちゃった。

ytakenakaytakenaka 2008/06/03 20:19 上の例はemacs 23でやってました。mewの作者さんだったんですね。失敬です。

kazu-yamamotokazu-yamamoto 2008/06/03 23:20 flet は、Emacs 22 もありますが、Common Lisp パッケージのマクロです。Common Lisp パッケージの利用には賛否両論があります。Emacs にはまともなパッケージの仕組みがないので、僕は Common Lisp を使って欲しくないと思っている派です。他のパッケージが Common Lisp パッケージを require していると、いろんな関数やマクロがグローバル空間に定義されてしまうからです。

また、flet マクロを展開してみると分りますが、flet では定義したい関数の実体を一旦退避し、グローバル空間で上書きして利用した後、元に戻しています。一瞬ですが、グローバル空間は汚れてしまうのです。

ytakenakaytakenaka 2008/06/03 23:29 なるほど、それであえてfletを外してかんがえようとされていたのですね。事情はよく知らなかったけどわかりました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証