後置演算マクロを定義するマクロ

後置インクリメントのような演算をマクロとして手軽に定義できるようにする、メタ・マクロ。
使い方は、3つの引数(定義するマクロ名、使用する演算名、第2オペランドのデフォルト値)を渡すだけ。演算名は単に貼り付けられるだけなので、マクロやラムダ式でも良い。
今回のバージョンでは、変数 place が delta の初期化部分でキャプチャできる(というか、できてしまう)。注意が必要かもしれない。


コード:

(defmacro define-post-operator (name op default)  
  `(defmacro ,name (place &optional (delta ,default))
     (multiple-value-bind (dummies vals new setter getter)
         (get-setf-expansion place)
       `(let* (,@(mapcar #'list dummies vals)
               (,(car new) ,getter))
          (prog1 ,(car new)
            (setq ,(car new) (,',op ,(car new) ,delta))
            ,setter)))))


使用例:

CL-USER> (define-post-operator =++ + 1)
=++
CL-USER> (let ((x 0))
           (list x (=++ x) x))
(0 0 1)
CL-USER> (define-post-operator =** expt 2)
=**
CL-USER> (let ((x 3))
           (list x (=** x) x))
(3 3 9)
CL-USER> (let ((v #(1 2 3)))
           (list (=** (svref v 1) 3)
                 v))
(2 #(1 8 3))