根据a macro-related question I recently posted to SO的建议,我通过调用函数(here is the standalone code in pastebin)编写了一个名为“fast”的宏:
(defun main ()
(progn
(format t "~A~%" (+ 1 2 (* 3 4) (+ 5 (- 8 6))))
(format t "~A~%" (fast (+ 1 2 (* 3 4) (+ 5 (- 8 6)))))))
这适用于REPL,在SBCL和CMUCL下:
$ sbcl
This is SBCL 1.0.52, an implementation of ANSI Common Lisp.
...
* (load "bug.cl")
22
22
$
不幸的是,代码不再编译:
$ sbcl
This is SBCL 1.0.52, an implementation of ANSI Common Lisp.
...
* (compile-file "bug.cl")
...
; during macroexpansion of (FAST (+ 1 2 ...)). Use *BREAK-ON-SIGNALS* to
; intercept:
;
; The function COMMON-LISP-USER::CLONE is undefined.
所以看起来通过在编译时让我的宏“快速”调用函数(“clone”,“operation-p”),我在Lisp编译器中触发了问题(在CMUCL和SBCL中验证)。
关于我做错了什么和/或如何解决这个问题的任何想法?
答案 0 :(得分:4)
关于您的代码的一些评论。
对象的多次测试可以用MEMBER
使用以下逗号的反引号不执行任何操作。你可以删除它。
您可以通过以下方式确保您的函数可用于宏:a)将这些函数移动到其他文件并在使用宏之前编译/加载,b)使用EVAL-WHEN
通知编译器来评估函数的定义或c)将函数作为本地函数添加到宏
示例:
(defmacro fast (&rest sexpr)
(labels ((operation-p (x)
(member x '(+ - * /)))
(clone (sexpr)
(if (consp sexpr)
(destructuring-bind (head . tail) sexpr
(if (operation-p head)
`(the fixnum (,head ,@(clone tail)))
(cons (clone head) (clone tail))))
sexpr)))
(car (clone sexpr))))
请注意,此版本和FAST
版本不是完整的代码助手。它们只识别简单的函数调用(而不是其他Lisp构造,如LAMBDA,LET,FLET,LABELS等)。
答案 1 :(得分:2)
没关系,我想通了:我必须将宏调用的函数(因此在编译期间需要)移动到一个单独的文件中,首先“编译文件”,“加载”它,然后“编译 - 文件“与宏的那个。
答案 2 :(得分:1)
在编译期间(通常)发生宏扩展绑定。
这意味着在宏扩展期间使用的任何函数(注意,不一定 in 宏扩展,返回值都是如此)必须在编译期间遇到宏时定义。 / p>