自分自身を表示するプログラム

よくC言語のパズルで取り上げられる題材(自己言及的プログラム、自己再生プログラムとも呼ばれる)。Lispだと特に技巧的なことは必要なくて、

(defun self-print ()
  (append (list 'defun 'self-print)
          (cdr (symbol-function 'self-print))))

のようにごく普通に書ける。

さらに、自己言及的関数を生成する関数なんてのも作れる。

(defun make-self-print (symbol)
  `(defun ,symbol nil
    (append (list 'defun ',symbol)
	    (cdr (symbol-function ',symbol)))))

たとえば、

(make-self-print 'self-print)

を評価すると、

(defun self-print nil (append (list (quote defun) (quote self-print)) (cdr (symbol-function (quote self-print)))))

これを評価してさらに

(self-print)

を評価すると、

(defun self-print nil (append (list (quote defun) (quote self-print)) (cdr (symbol-function (quote self-print)))))

自己言及的関数を判別する関数も定義できる。

(defun self-print-p (symbol)
  (equal (funcall symbol)
	 (append (list 'defun symbol)
		 (cdr (symbol-function symbol)))))
(self-print-p 'self-print)
==>t

となる。

Lispにはsymbol-functionなんていうある意味反則級の関数が用意されているので、こんな風に簡単に書ける。
結局、この手の問題はコンパイラインタプリタにどの程度の機能が用意されているかに依存するので、本質的にはパズルでもなんでもない。(極端な話、0番地を呼び出すと呼び出し元を逆アセンブルするシステムコールが発行されるようなOSを作れば、機械語でもcall 0;で5バイトだ*1 )

*1:xor eax,eax;call eax;なら、さらに短くて4バイトで済む。