2

此函数编译时出现警告,第一行中定义了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)
4

2 回答 2

7

如果要调用作为参数传递或分配给变量的函数,只需使用变量或参数作为第一个参数funcall

(defun test-function(fn)
  (funcall fn))

(test-function #'+)
;; => 0

该符号#'X是 , 的缩写(function X)(参见手册),其中X 必须是函数的名称,例如用defunorlabelsflet或 lambda 表达式定义。因此,#'fn不起作用,因为fn不是函数的名称,而是变量(在本例中为参数)。

Common-Lisp 是一个Lisp-2,即函数的命名空间不同于其他变量的命名空间。因此,函数的名称是特殊的,因为您可以直接在表单中调用它们,而如果将函数分配给变量,则必须使用(funcall name-of-the-variable arguments).

于 2016-09-25T20:49:02.010 回答
4

此函数编译时带有警告

请注意,这些只是警告:

CL-USER> (defun test-function (fn)
           (funcall #'fn))
  1. FN未使用该变量。
  2. 该函数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)表明您可能想要调用一个函数对象,但实际上您是通过其名称调用一个函数。

于 2016-09-26T07:21:49.287 回答