我刚开始学习宏功能的概念。
我的老师要求我们创建一个功能与incf
完全相同的宏功能。
以下是他为pop提供的一个例子
(defmacro mypop (nom)
(list 'prog1 (list 'car nom) (list 'setq nom (list 'cdr nom))) )
这是我试图变成宏的常规函数:
(defun iincf (elem &optional num )
(cond
((not num) (setq elem (+ 1 elem)))
(t (setq elem (+ num elem))) ) )
这是我尝试将其变成宏:
(defmacro myincf (elem &optional num )
(list 'cond
((list 'not num) (list 'setq elem (list '+ 1 elem)))
(t (list 'setq elem (list '+ num elem))) ) )
然而,我收到此错误,我不知道原因:
*** - system::%expand-form: (list 'not num) should be a lambda expression
另外,我不确定我的函数是否会实际更改顶级变量的值。
所以这是我的两个问题:
答案 0 :(得分:4)
错误的原因是您的语法无效:
((list ...) ...)
(t (list ...))
第一个元素应该是函数名或lambda表达式,因此您需要将其更改为
(list (list ...) ...)
(list t (list ...))
虽然宏还不是一个非常好的宏。首先,backquote语法会使代码更具可读性。它允许您编写仅评估指定表单的模板。例如,给定的MYPOP
宏看起来像
(defmacro mypop (nom)
`(prog1 (car ,nom)
(setq ,nom (cdr ,nom))))
仅评估在它们之前带有逗号的表单。与您的宏相同:
(defmacro myincf (elem &optional num)
`(cond
((not ,num) (setq ,elem (+ 1 ,elem)))
(t (setq ,elem (+ ,num ,elem)))))
COND
不应该成为扩张的一部分。它应该在宏展开期间进行评估,并且只返回其中一个分支的SETQ
表单。
(defmacro myincf (elem &optional num)
(cond
((not num) `(setq ,elem (+ 1 ,elem)))
(t `(setq ,elem (+ ,num ,elem)))))
两个分支之间的唯一区别是第一个分支默认为1
NUM
。实现相同目标的一种更简单的方法是给NUM
一个默认值。
(defmacro myincf (elem &optional (num 1))
`(setq ,elem (+ ,num ,elem)))
当然,标准INCF
有点复杂,因为它适用于各种地方(不仅仅是变量),并确保地点的子表单只被评估一次。但是,由于MYPOP
示例并未处理这些问题,因此我不认为您必须这样做。
如果您愿意,可以使用一种简单的方法来定义这样的宏
(define-modify-macro myincf (&optional (num 1)) +)
或者您可以使用
等手动进行相同的操作(defmacro myincf (place &optional (num 1) &environment env)
(multiple-value-bind (dummies vals store setter getter)
(get-setf-expansion place env)
`(let* (,@(mapcar #'list dummies vals)
(,(first store) (+ ,getter ,num)))
,setter)))
但在实际程序中使用DEFINE-MODIFY-MACRO
会更受欢迎(代码更短,错误更少)。如果您有兴趣,可以阅读GET-SETF-EXPANSION
和DEFINE-MODIFY-MACRO
。