通过以下 Common Lisp 最小示例代码(来自更复杂的代码库),我收到有关行的警告,((fboundp (car ',arg)) ,arg)
但我无法解释为什么或如何避免它们。
(defmacro cond-issue (&key (arg '(a b c)))
(let ((arg-var (gensym "arg-var-")))
`(let ((,arg-var (cond ((not (listp ',arg)) (list 'arg))
((fboundp (car ',arg)) ,arg)
(t ',arg))))
`(apply #'+ ,,arg-var))))
(cond-issue :arg (e f g))
CCL 抱怨:
;Compiler warnings :
; In an anonymous lambda form: Undefined function E
; In an anonymous lambda form: Undeclared free variable F
; In an anonymous lambda form: Undeclared free variable G
SBCL 抱怨:
; in: cond-issue :arg
; (E F G)
;
; caught style-warning:
; undefined function: common-lisp-user::e
;
; caught warning:
; undefined variable: common-lisp-user::f
;
; caught warning:
; undefined variable: common-lisp-user::g
;
; compilation unit finished
; Undefined function:
; e
; Undefined variables:
; f g
; caught 2 WARNING conditions
; caught 1 STYLE-WARNING condition
CLISP 并不抱怨。
使用 SBCL,我尝试了两种方法:编译和评估代码,警告方面没有区别。 [编辑:我从答案中得知我做错了]
这将是第二种cond
形式的一个(愚蠢的)示例用例。
(cond-issue :arg (car '(e f g)))
;; ⇒ (apply #'+ e)
CCL 和 SBCL 为何抱怨?
我该如何编写代码来满足CCL和SBCL?
我不太清楚这个宏的目的,但我认为有关这些警告的性质的问题可以得到解决。我在答案的末尾添加了一些关于可能的重新设计的注释。
SBCL和CCL抱怨的原因是他们正在编译代码。CLISP repl 解释代码。如果您打开一个新的 SBCL repl 并置于
eval
解释模式,则可以调用 OP 宏而不会触发警告:回到编译模式后,
eval
会出现警告:查看上面的宏展开,可以看出要编译的代码的一个分支包含一个带有未定义函数和两个未定义变量的函数调用:
((FBOUNDP (CAR '(E F G))) (E F G))
。编译器会抱怨,因为它无法编译此代码。我认为这是您真正想要的行为,即您可能不应该尝试规避编译器对未定义函数和变量的警告。考虑一些应该编译的代码:
这里的形式
(LIST 1 2)
已经取代了以前的形式(E F G)
,这对编译器来说没有问题。请注意,上面的结果形式是(APPLY #'+ (1 2))
,这不是合法的 Lisp 代码。我认为其意图是生成(APPLY #'+ '(1 2))
。此外,调用(cond-issue :arg 42)
产生了(APPLY #'+ (ARG))
我怀疑所(APPLY #'+ '(42))
期望的结果。cond-issue
这是修复这些问题的稍微修改的版本:再说一次,我认为您不应该尝试规避这些编译器警告,但作为一个实验,并本着修补半成品解决方案的精神,您可以包装在一个宏中,首先检查参数
cond-issue
:该解决方案有一些注意事项:
boundp
并fboundp
检查全局环境中的绑定,但它们无法检查本地绑定(并且我不确定是否有任何简单的方法可以做到这一点)。此外,包装器将拒绝宏调用,例如(cond-issue-check :arg 42)
出现错误。可以增强包装cond-issue-check
器以更好地验证输入,但当前的定义应该足以说明这一点。现在是询问宏的目的、是否需要宏并重新审视设计的好时机。在上面的最后一个示例中,检查包装器无法确定 和 的本地绑定是否
a
已b
绑定。cond-issue
当需要本地绑定时,可以调用原始的未包装的,但这意味着可能会因未绑定的函数或变量触发警告。无论如何,上述修复不会停止所有缺少定义的警告。一般来说,我不建议尝试抑制这些类型的警告,也不建议使用
cond-issue-check
. 最好让编译器完成它的工作。重新设计宏观
OP 的目标似乎是采用描述
#'+
可应用的其他表单的输入表单,并由此创建不需要在当前环境中有效的表单。如果是这种情况,这里有一个替代设计。这里,如果输入表单的第一个元素是 fbound 的符号,则
#'+
生成适用于调用输入表单的结果的表单。#'+
否则,将生成适用于所有参数的形式。一些调用示例:
如前所述,
fboundp
检查全局绑定,但它无法判断词法变量是否绑定到函数,即那些使用flet
or创建的变量labels
。以下示例展示了如何使用全局函数来cond-something
生成预期的输出,以及如何使用局部函数来cond-something
生成人们可能天真地期望的代码:添加:
CLISP 也抱怨道:
现在让我们编译函数`TEST:
然后您需要查看宏展开式:
然后看adabsurdum的答案...