我是一名初级 Lisp 程序员,我正在阅读 Practical Common Lisp 书,特别是第 9 章。
完成本章后,我尝试扩展单元测试环境。具体来说,我想要一个宏来获取函数名称以及输入和输出列表,而不是手动比较它们。即
(check-i-o 1+ (2 3) (3 4)) => (check (= (1+ 2) 3) (= (1+ 3) 4))
我承认我对 backtiks 的语法仍然有点困惑,这可能就是问题所在,但这是我尝试编写的以下代码
(defmacro check-i-o (function &body inputs-outputs)
`(check
,@(loop for i-o in inputs-outputs collect `(= (,function (first i-o)) (second i-o)))))
出于某种原因,每当我尝试在示例上运行此宏(例如,
(check-i-o 1+ (2 3) (3 4))
)时,我都会遇到错误The variable I-O is unbound.
如果相关:我在 Windows 上的 portacle emacs 中使用 slime-repl sbcl。
非常感谢您的帮助!
我已经尝试了所提供的代码的多种变体(主要是更改反引号,在调用宏时使用#'1+而不是1+,并尝试从宏的减速中删除 &body...)但没有任何帮助,我我很茫然...
macroexpand
您的示例代码来调试您的问题:
> (macroexpand '(check-i-o 1+ (2 3) (3 4)))
(CHECK
(= (1+ (FIRST I-O)) (SECOND I-O))
(= (1+ (FIRST I-O)) (SECOND I-O)))
生成的代码包含对
I-O
的引用,这是一个在计算表达式时未绑定的变量。它是宏内部绑定的变量,但您直接在结果表达式中插入了 I-O
符号。这就是你有错误的原因。
反引用/取消引用机制是一种生成表单的方法,其中某些部分被评估而其他部分则不被评估:除了未引用的子项之外,反引用的内容不会被评估。
在你的代码中,你写:
`(= (,function (first i-o)) (second i-o)))
仅将
function
变量的内容插入结果表单中。其余部分未加引号。您需要写:
`(= (,function ,(first i-o)) ,(second i-o)))
另一个改进是利用 LOOP 允许您为您进行模式匹配(又名解构)列表的事实:
,@(loop
for (i o) in inputs-outputs
collect `(= (,function ,i) ,o))))