Jon*_*h P 1 lisp macros common-lisp
我刚刚开始学习宏功能的概念。
我的老师要求我们创建一个宏函数,该函数的功能与incf
。
这是他给我们流行的一个例子
(defmacro mypop (nom)
(list 'prog1 (list 'car nom) (list 'setq nom (list 'cdr nom))) )
Run Code Online (Sandbox Code Playgroud)
这是我要变成宏的常规函数:
(defun iincf (elem &optional num )
(cond
((not num) (setq elem (+ 1 elem)))
(t (setq elem (+ num elem))) ) )
Run Code Online (Sandbox Code Playgroud)
这是我尝试将其转换为宏的尝试:
(defmacro myincf (elem &optional num )
(list 'cond
((list 'not num) (list 'setq elem (list '+ 1 elem)))
(t (list 'setq elem (list '+ num elem))) ) )
Run Code Online (Sandbox Code Playgroud)
但是,出现此错误,我不知道为什么:
*** - system::%expand-form: (list 'not num) should be a lambda expression
Run Code Online (Sandbox Code Playgroud)
另外,我不确定我的函数是否会实际更改顶级变量的值。
所以这是我的两个问题:
PS:我知道此练习可能会违反Lisp的许多通用规则,但这只是为了练习。谢谢!:)
错误的原因是您的语法无效:
((list ...) ...)
(t (list ...))
Run Code Online (Sandbox Code Playgroud)
第一个元素应该是函数名称或lambda表达式,因此您需要将其更改为类似
(list (list ...) ...)
(list t (list ...))
Run Code Online (Sandbox Code Playgroud)
尽管该宏不是一个很好的宏。首先,反引号语法将使代码更具可读性。它允许您编写仅评估指定表单的模板。例如,给定的MYPOP
宏看起来像
(defmacro mypop (nom)
`(prog1 (car ,nom)
(setq ,nom (cdr ,nom))))
Run Code Online (Sandbox Code Playgroud)
仅对带有逗号的表单进行评估。与您的宏相同:
(defmacro myincf (elem &optional num)
`(cond
((not ,num) (setq ,elem (+ 1 ,elem)))
(t (setq ,elem (+ ,num ,elem)))))
Run Code Online (Sandbox Code Playgroud)
该COND
不是真的应该扩大,虽然一部分。应该在宏扩展期间对其进行评估,并且仅SETQ
返回其中一个分支的表单。
(defmacro myincf (elem &optional num)
(cond
((not num) `(setq ,elem (+ 1 ,elem)))
(t `(setq ,elem (+ ,num ,elem)))))
Run Code Online (Sandbox Code Playgroud)
这两个分支之间的唯一区别是第一个默认为1
for NUM
。实现此目的的一种更简单的方法是提供NUM
默认值。
(defmacro myincf (elem &optional (num 1))
`(setq ,elem (+ ,num ,elem)))
Run Code Online (Sandbox Code Playgroud)
当然,该标准INCF
稍微复杂一点,因为它适用于各种场所(不仅限于变量),并确保场所的子表单仅被评估一次。但是,由于该MYPOP
示例无法处理这些问题,因此我也不认为您也必须这样做。
如果愿意,定义这种宏的简单方法是
(define-modify-macro myincf (&optional (num 1)) +)
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用类似的方法手动执行相同操作
(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)))
Run Code Online (Sandbox Code Playgroud)
但是DEFINE-MODIFY-MACRO
,在实际程序中最好使用它(缩短代码,减少错误)。你可以阅读GET-SETF-EXPANSION
和DEFINE-MODIFY-MACRO
如果你有兴趣。