Lisp 宏中使用循环 for-collect 的未绑定变量

问题描述 投票:0回答:1

我是一名初级 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...)但没有任何帮助,我我很茫然...

lisp common-lisp sbcl
1个回答
0
投票

您可以

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))))
© www.soinside.com 2019 - 2024. All rights reserved.