10.6 通用化引用 (Generalized Reference)
由于一个宏调用可以直接在它出现的地方展开成代码,任何展开为 setf
表达式的宏调用都可以作为 setf
表达式的第一个参数。 举例来说,如果我们定义一个 car
的同义词,
(defmacro cah (lst) `(car ,lst))
然后因为一个 car
调用可以是 setf
的第一个参数,而 cah
一样可以:
> (let ((x (list 'a 'b 'c)))
(setf (cah x) 44)
x)
(44 B C)
撰写一个展开成一个 setf
表达式的宏是另一个问题,是一个比原先看起来更为困难的问题。看起来也许你可以这样实现 incf
,只要
(defmacro incf (x &optional (y 1)) ; wrong
`(setf ,x (+ ,x ,y)))
但这是行不通的。这两个表达式不相等:
(setf (car (push 1 lst)) (1+ (car (push 1 lst))))
(incf (car (push 1 lst)))
如果 lst
是 nil
的话,第二个表达式会设成 (2)
,但第一个表达式会设成 (1 2)
。
Common Lisp 提供了 define-modify-macro
作为写出对于 setf
限制类别的宏的一种方法 它接受三个参数: 宏的名字,额外的参数 (隐含第一个参数 place
),以及产生出 place
新数值的函数名。所以我们可以将 incf
定义为
(define-modify-macro our-incf (&optional (y 1)) +)
另一版将元素推至列表尾端的 push
可写成:
(define-modify-macro append1f (val)
(lambda (lst val) (append lst (list val))))
后者会如下工作:
> (let ((lst '(a b c)))
(append1f lst 'd)
lst)
(A B C D)
顺道一提, push
与 pop
都不能定义为 modify-macros,前者因为 place
不是其第一个参数,而后者因为其返回值不是更改后的对象。
当前内容版权归 readthedocs 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 readthedocs .