【问题标题】:Is defun or setf preferred for creating function definitions in common lisp and why?在通用 lisp 中创建函数定义时首选 defun 或 setf 吗?为什么?
【发布时间】:2017-06-22 18:20:28
【问题描述】:

使用defunsetf 定义的函数的根本区别是什么?

使用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

如果按照Wikipedia

命名函数是通过使用 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

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

【问题讨论】:

    标签: macros lisp common-lisp


    【解决方案1】:

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

    第二,是的,(setf fdefinition)defun有很大的区别。

    “小的”区别在于defun 还可以设置函数名称的doc string(实际上,取决于您的实现方式,它也可以使用 lambda 来实现),并创建一个名为 block (在下面的宏扩展中看到)如果你愿意,你必须自己创建。

    最大的区别在于编译器“知道”defun 并将对其进行适当的处​​理。

    例如,如果您的文件是

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

    那么编译器可能会警告您在 bar 中调用 foo 时使用了错误数量的参数:

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

    如果将defun foo 替换为(setf (fdefinition 'foo) (lambda ...)),编译器不太可能会小心处理它。此外,您可能会收到类似

    的警告

    以下函数已使用但未定义: 喂

    您可能想通过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))
    

    【讨论】:

    • DEFUN 也会创建一个命名块。必须将其添加到 lambda。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多