with-gensyms
clispのgen-symsマクロは、生成するシンボル名が第一引数で、gen-symsで生成されるシンボルが格納される変数名は、第二引数以降が該当するみたい。
(defmacro (prefix &rest sym-names) &body body)みたいな、シグネチャーで定義されているみたい。
実践Common Lispで前提としているwith-gensymsマクロは、(defmacro (&rest sym-names) &body body)というシグネチャーとしているようだった。他の処理系では、こうなってるのかな。 http://d.hatena.ne.jp/smeghead/20090517/pcl
CLISPには拡張マクロとして用意されてるんだ・・・。知らなかった。
実践Common Lisp(僕は持ってないので確認してませんが)に載っているマクロは、引数を見る限りポールグレアムが書いたやつそのままみたい(他の処理系でこの形が採用されているかは知らない)。このマクロは便利なわりに結構単純で、
CL-USER> (defmacro with-gensyms (syms &body body) `(let ,(mapcar #'(lambda (s) (list s '(gensym))) syms) ,@body))
のように定義できます。
で、CLISPで用意されている拡張マクロ(with-gensyms)とやらの仕様は、
Similar to its namesake from Paul Graham's book “On Lisp”, this macro is useful for writing other macros:
(with-gensyms ("FOO-" bar baz zot) ...)expands to(let ((bar (gensym "FOO-BAR-") )31.11. Additional Fancy Macros and Functions
(baz (gensym "FOO-BAZ-"))
(zot (gensym "FOO-ZOT-")))
...)
とのことですので、このように定義できるんじゃないかと。*1
CL-USER> (defmacro with-gensyms2 ((prefix &rest syms) &body body) (labels ((get-prefix (s) (concatenate 'string prefix (symbol-name s) "-") )) `(let ,(mapcar #'(lambda (s) `(,s (gensym ,(get-prefix s)))) syms) ,@body)))
振る舞いとしてはほとんど何も変わらなくて、gensym関数に値を渡しているかどうか。
CL-USER> (macroexpand '(with-gensyms (a b) (setq a 10 b 20))) ;=> (LET ((A (GENSYM)) (B (GENSYM))) (SETQ A 10 B 20)) CL-USER> (macroexpand '(with-gensyms2 ("FOO-" a b c) (setq a 10 b 20 c 30))) ;=> (LET ((A (GENSYM "FOO-A-")) (B (GENSYM "FOO-B-")) (C (GENSYM "FOO-C-"))) ; (SETQ A 10 B 20 C 30))
gensym関数は一般的には引数なしで使うけど、文字列を渡すとそれをプレフィックスとして使うようになる。
CL-USER> (gensym) #:G4400 ;; デフォルトだとGというプレフィックスが使われる CL-USER> (gensym "LISP") #:LISP4401