「LISPといえばS式だが、昔はM式というものがあった」 という話はなぜか多くの人が知っている [要出典] 。 しかしM式は人間が読み書きするものであり、計算機が読み書きするものではない (APPLE-LISPなどM式を読めるLISPも一応存在する)。 昔の計算機のためのLISPプログラムの記述法を知る人は少ない [要出典] 。 具体的には吸血鬼関数の話を読んだ人から 「(このプログラムって)S式じゃないんですか?」という質問がやってきた。 そんなわけで昔のLISPプログラムの記述法の話をする。
今どきのLispにおける (car '(a b c))
は、
昔のLISPだと CAR ((A B C))
のように書いた。
これだけだとよく分からないのでもっと例をあげる。
(cons 1 2) ;; 今
CONS (1 2) ;; 昔
(cons 'a 'b) ;; 今
CONS (A B) ;; 昔
(cons 'a '(b)) ;; 今
CONS (A (B)) ;; 昔
(car '(a b c)) ;; 今
CAR ((A B C)) ;; 昔
((lambda (x) (cons x x)) 1) ;; 今
(LAMBDA (X) (CONS X X)) (1) ;; 昔
((lambda (x) (cons 'x x)) 'a) ;; 今
(LAMBDA (X) (CONS (QUOTE X) X)) (A) ;; 昔
((lambda (x) (cons (car x) x)) '(a b)) ;; 今
(LAMBDA (X) (CONS (CAR X) X)) ((A B)) ;; 昔
最初の CONS (1 2)
から想像が付くように、
今どきのLispだと (関数 引数1 引数2)
と書くところを、
昔のLISPは 関数 (引数1 引数2)
と書いた。
次の CONS (A B)
から推測できるが、引数は勝手にクォートされる。
リストも当然クォートされるので CAR ((A B C))
のような書き方になる。
CAR (A B C)
と書くとCARに3つの引数を渡すことになってしまうので注意。
LAMBDAが出てくると少し面白くなってくる。基本は同じで
((LAMBDA ...) 引数1 引数2)
は (LAMBDA ...) (引数1 引数2)
となる。
面白いのはLAMBDAの内側だ。内側に関しては今と全く同じ記法になる。
具体的には (関数 引数1 引数2)
という記法で、
引数が勝手にクォートされることはない。
この古い記法を使うLISPはEvalquote Lispと呼ばれる。 今どきの記法を使うLispはEval Lispと呼ばれる。 Eval Lispは入力した式がevalによって評価される。 そこから想像できるように、 Evalquote Lispは入力した式がevalquoteによって評価される。
evalquoteの定義はLISP 1.5 Programmer’s Manualでは次のように書かれている。
evalquote[fn;x] = apply[fn;x;NIL]
EVALQUOTEとはAPPLYである
ちなみにAPPLYの第3引数は環境を表す連想リストだ。 今どきのLispにはないことを考えると、evalquoteは完全にapplyだと言える。
今どきの記法はevalに合わせたもの、古い記法はapplyに合わせたものと考えると、 すべて納得がいくだろう。 関数と引数を分けて書くのは当然。 引数がクォートされるというのも納得がいく。 applyは受け取った引数を評価したりしないからだ。
しかし、なぜevalquoteなどという関数を導入したのだろうか。
具体的な説明を見つけることはできなかったが、
LISP I Programmer’s Manualを読めば少し想像ができる。
LISP IはEval LispでもなければEvalquote Lispでもない。
強いて名前をつけるならApply Lispだろう。
LISP Iでは関数と引数と環境の3つ組を入力する。
例えば CAR ((A B C)) ()
のように書く。
3つ組はそのままAPPLYに渡される。
LISP I Programmer’s Manualには
“The APPLY operator is the interpreter part of the LISP system”
とはっきりと書かれている。昔のLISPはevalではなくapplyが主役だったのだ。
しかしながらグローバル変数があれば環境が役に立つ場面はあまりなさそうだ。 そう考えると毎回NILを入力するのもバカバカしいし、 evalquoteというものが導入されたのも分からなくもない。
LISP Iは関数と引数と環境の3つ組を受け取った。 LISP 1.5は関数と引数の2つ組を受け取った。 そして今どきのLispは式のみ(1つ組)を受け取る。 そう考えると将来のLispは何も受け取らないと考えるのが自然だろう。 対話環境でLispが対話を拒み一方的に話し続けると考えると面白そうだ。 詳細な設計は読者の練習問題とする。
2022-06-24