On lisp 7章 「マクロ」
突然ですが、On lispを読んでいます。
http://www.komaba.utmc.or.jp/~flatline/onlispjhtml/index.html
Web版は↑にあります。
マクロはどのように動作するか
逆クォート
- クオート(')、バッククオート(`)、カンマ(,)、カンマアット(,@)
'(1 2 3) := `(1 2 3) `(a ,b c ,d) := (list 'a b 'c d) (let ((a 1) (b 2)) `(0 ,a ,b 3)) -> (0 1 2 3) ;結構便利
単純なマクロの定義
- ok
マクロ展開の確認
- macroexpand-1用のマクロ
(defmacro mac (expr) `(pprint (macroexpand-1 ',expr))) ;これは便利かも。emacs+slimeなら式の先頭で C-c Return でいけるけど、時々動かないことがある。
引数リストの構造化代入
- ok
マクロのモデル
(defmacro our-expander (name) `(get ,name 'expander)) (defmacro our-defmacro (name parms &body body) (let ((g (gensym))) `(progn (setf (our-expander ',name) #'(lambda (,g) (block ,name (destructuring-bind ,parms (cdr ,g) ,@body)))) ',name))) (defun our-macroexpand-1 (expr) (if (and (consp expr) (our-expander (car expr))) (funcall (our-expander (car expr)) expr) expr))
- これは面白い。
- シンボルにプロパティーなんてあったんだ。初めて知った。
- http://ancient.s6.xrea.com/lisp/cookbook.html#sec6
- expanderプロパティーに入れるということらしい。
- sbclではこのままだとうまくいかなくて、以下のようにしないとだめだった。
>(defmacro defmacro2 (name parms &body body) (let ((g (gensym))) `(progn (setf (macro-function ',name) #'(lambda (,g env) (declare (ignorable env)) (block ,name (destructuring-bind ,parms (cdr ,g) ,@body)))) ',name))) >(defmacro2 abc (x y z) `(+ ,x ,y ,z)) >(abc 1 2 3) ;=> 6
- slimeでの引数表示は狂ってしまうけど、うまくいく。
- あと、destructuring-bindは&restとか&keyとかも判別できることを始めて知った。
- ただリストの形をみて分配するだけかと思ってた。
>(destructuring-bind (a &rest b &key c d) '(1 :c 2 :d 3) (format t "a:~a~%b:~a~%c:~a~%d:~a~%" a b c d)) ; => ; a:1 ; b:(C 2 D 3) ; c:2 ; d:3
- なるー。
プログラムとしてのマクロ
(let ((a 1)) (setq a 2 b a) (list a b)) ;=>(2 2) (let ((a 1)) (psetq a 2 b a) (list a b)) ;=>(2 1)
- setqとpsetqの違い。
- 初めて知った。
マクロのスタイル
- 人間に読まれる部分は可読性を優先すべし。マクロ内部は効率を優先すべし。
マクロへの依存
- マクロは関数のように勝手に更新されないから、定義変更したら再読込みさせろよって話。
関数からマクロへ
- 関数をマクロに直せるよって話。
- 話はわかるが、こういう細かい関数をマクロにしていつも失敗してる気がする。
- mapcarとかapplyとかに投げられないし、結局関数に戻すことが多い。
- 最近は、マクロを使うのは新しい文法を作る時や、速度を稼ぎたいとき、マクロにしかできない時に限ってる。
シンボル・マクロ
- これ使ったことはないが面白いかもしれない。
- そんなに大きい関数書かないから使う場面もあんまなさそうではある。