刚开始学习macro遇到一点小问题,现在要定义一个宏函数variable-chain,可以接收任意多个参数,实现如下效果
lisp
(setf a 0) (setf b 0) (setf c 0) (variable-chain a b c) > a b > b c > c 0
即(variable-chain a b c d)将a的值置为b的变量名,将b的值置为c的变量名,以此类推
一开始我写的代码如下:
lisp
(defmacro variable-chain (&rest vars) `(do ((v vars (rest v))) ((null (rest v)) nil) (setf (car v) (cadr v))))
运行出现错误提示vars没有值,找不到错误的原因望高手指点
我先来解释你遇到的问题y,然后再回答你最初的问题x
宏参数中的符号要在反引用中使用,需要用逗号进行求值,所以在vars前加一个逗号即可
我们看看宏展开后是什么样子
当然,你的宏写的是有问题的,其实你真正想要的是rotatef
那么该如何写一个rotatef宏呢,写一个setf版本吧,
(rotatef a b c)
等价于(setf tmp a a b b c c tmp)
,我们需要一个临时变量(不一定名字叫做tmp)第一步我们先来生成
a a b b c c
这种形式第二步我们需要连接临时变量,临时变量的名称不要直接写tmp,要用gensym动态生成
,@可以吞掉一层括号,将
(A A B B C C)
变成A A B B C C
插入到setf里面看下生成的代码,#:G586是gensym动态生成的符号
考虑到更人性化一点
(our-rotatef)
如果直接被调用那不是变成(setf #:G586 #:G586)
了吗,由于#:G586前面还未赋值,这句直接执行是会出错的,出错信息有点莫名其妙加一个断言,让出错信息更明确一些
我的我的,我看你用setf那句以为你是要赋值呢,要将a的值设为b的符号名,你需要对b进行quote,例如
我们心中有了模板之后,就从1扩展到N
butlast和cdr对列表
(‘A A ’B B ‘C C)
掐头去尾,变成(A 'B B 'C)
再次宏展开查看
补充一点我的理解吧,有一些介绍LISP有7个基础操作符,利用这7个操作符可以写一个eval出来,也就是LISP实现了自举(一门语言用自己实现了自己)。在CommonLISP中,仅有这7个操作符是实现不了CommonLISP的,于是增加了更多的操作符,一共有25个(记不清了),被称为特殊操作符,这些操作符无法用宏或者函数来实现,必须是由编译器内置。
不清楚一个东西是宏、函数或者是特殊操作符的时候,可以笼统地称为过程,宏函数的叫法比较罕见