まくろちゃれんじ!setf編(前)

工場見学時にはやみずさんから入門Common Lisp―関数型4つの特徴とλ(ラムダ)計算をいただいた。わーい。でもLispの勉強は基本的にネットだったりする罠。
今日はマクロについて少しだけ勉強。macroexpandなんていう面白い関数を知ったので、ちょっと実験しました。

> (macroexpand '(setf (car x) 10))
(progn (rplaca x 10) 10)
> (macroexpand '(setf x 10))
(setq x 10)

以前ちょっと疑問に思ったsetf周辺のお話の調査をしてるわけであります。面白いね、setfは2通り以上のマクロ展開をするんだね。
さらに、rplacaの調査。

> (macroexpand '(rplaca x 10))
(rplaca x 10)

rplacaはマクロではないみたいですね。Common Lisp 入門によると、

Lisp にはコンスセルの CAR 部を直接書き換える関数 rplaca と、CDR 部を直接書き換える関数 rplacd が用意されています

ということなので、特別式と考えても良さそうです。RePLAce CArの略かな。


さて、先ほどのsetfの展開について考察。car xとxとで何が違ったのか。car xはアトムでxはリストってことではないかなと思ったものの、よく考えてみるとそれだけでは理由にならない。なぜならxがアトムではなく単リストの場合だって考えられるからだ。
参ったな。(setq x 10)としたときのxと(car x)とを区別する方法が思いつかん。どっちもリストではなくアトムだって評価されるし、どちらもシンボルではない。


ひとまずその辺を考えてないコードを載っけてみる。

(defmacro my-setf
  (obj val)
  (if (atom obj)
      `(progn
	(rplaca ,obj ,val)
	,val)
    `(setq ,obj ,val)))

カンマとかバッククオートの使い方についてはよく分かってないです。ただonLispで使ってたので真似してみただけ。
カンマがないとobjやvalがそのまんま文字列として出てきちゃうので、引数として展開するとかそう言う意味だろうとは思う。


いま調べたところ、バッククオートはカンマを使うことで選択的に引数を評価させられる効果を持っているとか。つまり、マクロでうまく使えば呼び出し側の外側の変数を利用することも可能であるってことかな。でもそれはレキシカルスコープ的ではないような。
ま、それはともかく。
バッククオートはマクロ以外で使う分にも便利そう。
ソース:Emacs Lisp