是否优先使用defun或setf来创建常见的lisp中的函数定义?为什么?

dec*_*ser 3 lisp macros common-lisp

使用defunsetf如下定义的函数的根本区别是什么,并且在样式考虑之外的另一种方法优先于另一种方法?

使用defun:

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

* (myfirst '(A B C))

A
Run Code Online (Sandbox Code Playgroud)

使用setf:

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

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

A
Run Code Online (Sandbox Code Playgroud)

如果按照维基百科的说法:

通过使用defun宏将lambda表达式存储在符号中来创建命名函数

使用setf以不同方式创建变量需要使用funcall:

* (defvar myfirst)

MYFIRST
* (setf myfirst (lambda (l) (car l)))

#<Interpreted Function (LAMBDA (X) (+ X X)) {48035001}>
* (funcall myfirst '(A B C))

A
Run Code Online (Sandbox Code Playgroud)

我的理解是这种类型的变量与前一种不同,因为这个变量在与defun绑定符号相同的命名空间中找不到,如为什么有多个命名空间?.

sds*_*sds 7

首先,永远不要低估风格的重要性.我们编写代码不仅仅是为了运行计算机,更重要的是,让人们阅读.使代码可读且易于理解是软件开发的一个非常重要的方面.

第二,是的,和之间存在很大差异.(setf fdefinition)defun

"小"差异也defun可以设置函数名称的doc字符串(实际上,取决于你的imeplementation如何工作,它也可以用lambda做),并创建一个命名block(见下面的宏扩展),你会否则必须创建自己,如果你想.

最大的区别是编译器"知道" defun并将适当地处理它.

例如,如果您的文件是

(defun foo (x)
  (+ (* x x) x 1))
(defun bar (x)
  (+ (foo 1 2 x) x))
Run Code Online (Sandbox Code Playgroud)

那么编译器可能会提醒你,你打电话foobar数错误的参数:

警告:在第3..4行的BAR中:使用3个参数调用FOO,但它需要1个参数.[FOO在第1..2行中定义]

如果替换defun foowith (setf (fdefinition 'foo) (lambda ...)),编译器不太可能小心处理它.而且,你可能会得到一个警告

使用了以下功能但未定义:FOO

您可能希望defun通过宏扩展来检查实现中的内容:

(macroexpand-1 '(defun foo (x) "doc" (print x)))
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

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)))
Run Code Online (Sandbox Code Playgroud)

这里的要点是defun有很多"引擎盖下",并且有一个原因.setf fdefinition另一方面,更多的是"你所看到的就是你得到的",即没有任何魔法.

这并不意味着setf fdefinition在现代的lisp代码库中没有位置.你可以使用它,例如,实现"穷人trace"(UNTESTED):

(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))
Run Code Online (Sandbox Code Playgroud)