此函数编译警告, fn 已定义且从未在第一行使用, fn 是第二行中未定义的函数:
(defun test-function (fn)
(funcall #'fn))
为什么呢?一般性解释或其链接将很棒。
PD:完成日志:
test.lisp:9:1:
style-warning:
The variable FN is defined but never used.
--> PROGN SB-IMPL::%DEFUN SB-IMPL::%DEFUN SB-INT:NAMED-LAMBDA
==>
#'(SB-INT:NAMED-LAMBDA TEST-FUNCTION
(FN)
(BLOCK TEST-FUNCTION (FUNCALL #'FN)))
test.lisp:10:3:
style-warning:
undefined function: FN
==>
(SB-C::%FUNCALL #'FN)
答案 0 :(得分:7)
如果要调用作为参数传递的函数或分配给变量,只需使用变量或参数作为funcall
的第一个参数:
(defun test-function(fn)
(funcall fn))
(test-function #'+)
;; => 0
符号#'X
是(function X)
的缩写,(请参阅manual),其中X
必须是函数的名称例如,使用defun
或labels
或flet
或lambda表达式定义。因此,#'fn
不起作用,因为fn
不是函数的名称,而是变量(在本例中是参数)。
Common-Lisp是Lisp-2,即函数的命名空间与其他变量的命名空间不同。因此,函数的名称是特殊的,你可以直接在一个表单中调用它们,而如果一个函数被分配给一个变量,它必须用(funcall name-of-the-variable arguments)
调用。
答案 1 :(得分:4)
此功能编译警告
请注意,这些只是警告:
CL-USER> (defun test-function (fn)
(funcall #'fn))
FN
。FN
未定义。让我们看一下这个函数:
(defun test-function (fn) ; this introduces a variable FN
(funcall #'fn)) ; here you use a function FN
由于范围中没有本地函数FN
,因此您使用的是全局函数FN
。在你的情况下,它没有定义。
您可以稍后定义全局函数FN
:
CL-USER> (defun fn ()
'foobar)
FN
这已经可以了,因为Common Lisp通常也会使用后期绑定来处理未定义的函数,并会在运行时查找该函数。
如果我们再次编译您的原始函数,那么您可以看到只剩下一个警告:
CL-USER> (defun test-function (fn) ; the variable FN is defined
(funcall #'fn)) ; the function FN is used
; The variable FN is defined but never used.
这是因为我们现在定义了一个全局函数FN
。
但是:调用一个全局函数可能不是你想要的,因为这可以更简单地写成:
(defun test-function (fn)
(fn)) ; calling the function `FN`.
FUNCALL 用于调用带参数的函数对象:
FUNCALL
的典型用例正在调用带参数的函数对象:
(funcall foo 1 2 3)
其中FOO
是绑定到函数的变量。
在您的情况下,这可能是:
CL-USER> (defun test-function (fn) ; a variable FN gets introduced
(funcall fn)) ; a variable FN gets used
请记住:(funcall #'foo ...)
看起来不对。
如果你的代码中有(funcall #'foo 1 2 3)
之类的东西,那么你可能做错了什么,因为它可以更容易地写成(foo 1 2 3)
。
因此使用(funcall #'foo 1 2 3)
是一个代码气味,表明你可能想调用一个函数对象,但实际上是通过它的名字调用一个函数。