Common Lisp, "определенный, но никогда не используемый"

Эта функция компилируется с предупреждениями, 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)

2 ответа

Решение

Если вы хотите вызвать функцию, переданную в качестве параметра или присвоенную переменной, просто используйте переменную или параметр в качестве первого аргумента для funcall:

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

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

Запись #'X это сокращение от (function X), (см. руководство), где X должно быть именем функции, например, определенной с помощью defun или же labels или же fletили лямбда-выражение. Так, #'fn не работает с fn это не имя функции, а переменная (в данном случае параметр).

Common-Lisp - это Lisp-2, то есть пространство имен функций отличается от пространства имен других переменных. Таким образом, имена функций являются особыми в том смысле, что вы можете вызывать их непосредственно в форме, в то время как, если функция назначена переменной, она должна вызываться с помощью (funcall name-of-the-variable arguments),

Эта функция компилируется с предупреждениями

Обратите внимание, что это только предупреждения:

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) указывает на то, что вы, вероятно, хотели вызвать объект функции, но на самом деле вы вызываете функцию через ее имя.

Другие вопросы по тегам