On lisp 7章 「マクロ」

突然ですが、On lispを読んでいます。
http://www.komaba.utmc.or.jp/~flatline/onlispjhtml/index.html
Web版は↑にあります。

マクロはどのように動作するか

  • マクロ定義とは実質的にLispコードを生成する関数である.
  • Lispのマクロは並外れて強力である.
    • なぜなら、パーサの出力はLispのオブジェクトのリストから成り, マクロを使えば,プログラムがパーサとコンパイラの間の中間形式のときに操作することができるからである.

逆クォート

  • クオート(')、バッククオート(`)、カンマ(,)、カンマアット(,@)
'(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))
  • 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とかに投げられないし、結局関数に戻すことが多い。
  • 最近は、マクロを使うのは新しい文法を作る時や、速度を稼ぎたいとき、マクロにしかできない時に限ってる。

シンボル・マクロ

  • これ使ったことはないが面白いかもしれない。
  • そんなに大きい関数書かないから使う場面もあんまなさそうではある。