Macros 在common lisp中创建函数定义时是否首选defun或setf?为什么?

Macros 在common lisp中创建函数定义时是否首选defun或setf?为什么?,macros,lisp,common-lisp,Macros,Lisp,Common Lisp,下面使用defun和setf定义的函数有什么根本区别?除了样式考虑之外,一种方法是否优于另一种方法 使用defun: * (defun myfirst (l) (car l) ) MYFIRST * (myfirst '(A B C)) A 使用setf: * (setf (fdefinition 'myfirst) #'(lambda (l) (car l))) #<FUNCTION (LAMBDA (L)) {10021B477B}> * (myfirst '(A

下面使用
defun
setf
定义的函数有什么根本区别?除了样式考虑之外,一种方法是否优于另一种方法

使用
defun

* (defun myfirst (l)
    (car l) )
MYFIRST

* (myfirst '(A B C))

A
使用
setf

* (setf (fdefinition 'myfirst) #'(lambda (l) (car l)))

#<FUNCTION (LAMBDA (L)) {10021B477B}>
* (myfirst '(A B C))

A

我的理解是,这种类型的变量与前面的不同,因为该变量与中所述的defun绑定符号不在同一名称空间中。

首先,我们不应低估样式的重要性。 我们编写代码不仅是为了让计算机运行,更重要的是,是为了让人们阅读。 使代码易读易懂是软件开发的一个非常重要的方面

第二,是的,和之间有很大的区别

“小”区别在于
defun
还可以设置函数名的名称(实际上,取决于IME实现的工作方式,它也可以使用lambda进行设置),并创建一个命名的(见下面的宏展开式),否则,您必须自己创建它

最大的区别在于编译器“知道”有关
defun
的信息,并将对其进行适当的处理

例如,如果您的文件是

(defun foo (x)
  (+ (* x x) x 1))
(defun bar (x)
  (+ (foo 1 2 x) x))
然后,编译器可能会警告您在
bar
中调用
foo
时参数数目错误:

警告:第3..4行中的条中:FOO是用3个参数调用的,但它需要1个参数 论点 [第1.2行定义了FOO]

如果将
defun foo
替换为
(setf(fdefinition'foo)(lambda…)
,编译器不可能如此小心地处理它。此外,您可能会收到一条类似以下内容的警告:

已使用但未定义以下功能: 福

您可能希望通过it检查
defun
在实现中的作用:

CLISP将其扩展为

(LET NIL (SYSTEM::REMOVE-OLD-DEFINITIONS 'FOO)
 (SYSTEM::EVAL-WHEN-COMPILE
  (SYSTEM::C-DEFUN 'FOO (SYSTEM::LAMBDA-LIST-TO-SIGNATURE '(X))))
 (SYSTEM::%PUTD 'FOO
  (FUNCTION FOO
   (LAMBDA (X) "doc" (DECLARE (SYSTEM::IN-DEFUN FOO)) (BLOCK FOO (PRINT X)))))
 (EVAL-WHEN (EVAL)
  (SYSTEM::%PUT 'FOO 'SYSTEM::DEFINITION
   (CONS '(DEFUN FOO (X) "doc" (PRINT X)) (THE-ENVIRONMENT))))
 'FOO)
SBCL负责:

(PROGN
 (EVAL-WHEN (:COMPILE-TOPLEVEL) (SB-C:%COMPILER-DEFUN 'FOO NIL T))
 (SB-IMPL::%DEFUN 'FOO
                  (SB-INT:NAMED-LAMBDA FOO
                      (X)
                    "doc"
                    (BLOCK FOO (PRINT X)))
                  (SB-C:SOURCE-LOCATION)))
这里的要点是,
defun
有很多“秘密”,这是有原因的<另一方面,code>setf fdefinition更多的是“你看到的就是你得到的”,也就是说,不涉及魔法

这并不意味着
setf fdefinition
在现代lisp代码库中没有位置。您可以使用它,例如,实现“穷人的”(未经测试):


DEFUN还将创建一个命名块。我们必须将其添加到lambda中。
(LET NIL (SYSTEM::REMOVE-OLD-DEFINITIONS 'FOO)
 (SYSTEM::EVAL-WHEN-COMPILE
  (SYSTEM::C-DEFUN 'FOO (SYSTEM::LAMBDA-LIST-TO-SIGNATURE '(X))))
 (SYSTEM::%PUTD 'FOO
  (FUNCTION FOO
   (LAMBDA (X) "doc" (DECLARE (SYSTEM::IN-DEFUN FOO)) (BLOCK FOO (PRINT X)))))
 (EVAL-WHEN (EVAL)
  (SYSTEM::%PUT 'FOO 'SYSTEM::DEFINITION
   (CONS '(DEFUN FOO (X) "doc" (PRINT X)) (THE-ENVIRONMENT))))
 'FOO)
(PROGN
 (EVAL-WHEN (:COMPILE-TOPLEVEL) (SB-C:%COMPILER-DEFUN 'FOO NIL T))
 (SB-IMPL::%DEFUN 'FOO
                  (SB-INT:NAMED-LAMBDA FOO
                      (X)
                    "doc"
                    (BLOCK FOO (PRINT X)))
                  (SB-C:SOURCE-LOCATION)))
(defun trace (symbol)
  (setf (get symbol 'old-def) (fdefinition symbol)
        (fdefinition symbol)
        (lambda (&rest args) 
          (print (cons symbol args))
          (apply (get symbol 'old-def) args))))
(defun untrace (symbol)
  (setf (fdefinition symbol) (get symbol 'old-def))
  (remprop symbol 'odd-def))