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

时间:2017-02-05 20:47:21

标签: macros lisp common-lisp

使用<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <img id="model" name="wiz" src="http://tive.pl/model.png" usemap="#wizualizacja"/> <img id="spodnie" name="wiz" src="http://tive.pl/spodnie.png" usemap="#wizualizacja"/> <map name="wizualizacja"> <area shape="poly" coords="119,800,114,758,111,737,111,736,106,719,102,706,98,689,95,670,92,657,91,639,93,637,93,636,96,636,98,636,100,636,101,636,103,636,104,636,106,635,106,635,107,634,107,635,108,635,109,636,111,637,113,637,114,637,116,637,117,637,119,637,120,637,122,637,124,637,126,637,128,638,130,638,133,638,136,638,140,638,142,638,144,637,145,635,147,634,148,631,149,630,151,618,153,609,153,601,154,599,156,605,159,609,160,611,161,613,162,614,164,616,168,619,173,621,177,622,181,623,185,623,187,623,188,623,188,634,267,634,267,620,285,620,285,642,285,643,285,644,286,644,286,646,285,667,284,675,283,691,283,698,282,701,282,705,282,707,282,720,282,727,282,730,282,733,283,739,283,742,283,743,282,753,282,756,282,758,283,760,284,766,285,769,285,771,285,774,285,776,286,778,286,779,286,781,286,787,287,789,287,792,288,794,288,796,288,798,288,800" alt="spodnie" title="spodnie" id="area"/> </map>defun定义的函数的基本区别如下所示,并且除了样式考虑之外,还有一种优先于另一种方法的方法吗?

使用setf

defun

使用* (defun myfirst (l) (car l) ) MYFIRST * (myfirst '(A B C)) A

setf

如果根据Wikipedia

  通过使用defun宏

将lambda表达式存储在符号中来创建命名函数

使用* (setf (fdefinition 'myfirst) #'(lambda (l) (car l))) #<FUNCTION (LAMBDA (L)) {10021B477B}> * (myfirst '(A B C)) A 以不同方式创建变量需要使用setf

funcall

我的理解是这种类型的变量与前一种变量不同,因为在Why multiple namespaces?中描述的与defun绑定符号相同的名称空间中找不到此变量。

1 个答案:

答案 0 :(得分:7)

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

其次,是的, (setf fdefinition)defun之间存在很大差异。

“小”差异是defun也可以设置函数名称的doc string(实际上,取决于你的imeplementation如何工作,它也可以用lambda来做),并创建一个名为block(见下面的宏扩展),如果你愿意,你可以自己创建。

最大的区别是编译器“知道”defun并且会适当地处理它。

,例如,如果您的文件是

(defun foo (x)
  (+ (* x x) x 1))
(defun bar (x)
  (+ (foo 1 2 x) x))

然后编译器可能会警告您,使用错误的参数数量在foo中调用bar

  

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

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

  

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

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

(macroexpand-1 '(defun foo (x) "doc" (print x)))

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有很多“幕后”,并且有一个原因。另一方面,setf fdefinition更多的是“你所看到的就是你得到的”,即没有任何魔法。

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

(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))