【问题标题】:How to declare a function before its definition in common lisp?如何在 common lisp 定义之前声明一个函数?
【发布时间】:2016-11-28 04:03:09
【问题描述】:

我将收到关于 SBCL 中 f2 的未定义函数警告,并带有以下代码示例。 是否可以像在 C 中一样首先声明 f2 以避免警告。 我用谷歌搜索,没有任何线索。

(defun f ()
  (print (f2)))

(defun f2 ()
  (print "f2"))

【问题讨论】:

    标签: lisp common-lisp


    【解决方案1】:

    如果在单个compilation unit 中定义它之前使用该函数,例如,同一个文件,那么编译器 不会抱怨未定义的函数(普通的load 仍然可能,所以先编译你的代码!)

    否则你可以declaimftype:

    (declaim (ftype (function () t) f2)
    

    意味着f2 不接受任何参数并返回一个t 类型的value

    但是,在定义已经加载的情况下编译使用函数的文件会更有意义。您可以(并且应该!)使用 asdf 作为特定于 Lisp 的 make(1):指定依赖项,以便编译器在编译用户时拥有所有函数的定义。

    【讨论】:

    • 非常感谢,我一直在寻找这个很长一段时间。 ;-) 它适用于声明。 - 顺便说一句,即使我将两个函数放在同一个文件中,sbcl 仍然会给出未定义函数警告。
    • @lll 您可能只是在加载文件,而不是先编译它。在 repl 中,使用 (load (compile-file "file.lisp")),或者从 Slime/Sly 中使用 C-c C-k
    • 实际上我认为标准所谓的“编译单元”对编译语义没有影响。文件中函数的编译器语义在稍后在同一个文件中定义之前被使用,与“编译单元”无关——它只是与文件编译的概念相关——而不是“编译单元”的概念' - 仅由 with-compilation-unit 模糊定义。
    • @RainerJoswig:我认为writeup 已经足够清晰了。
    • 该行为是可选的(可以扩展为 progn),并且没有很好地指定延迟警告的含义。这些实现实际上推迟了未定义的函数警告,并且只对那些未在编译文件中定义的函数发出警告——但在我看来,这个功能在标准中或多或少地未指定。例如,当 sbcl 最终看到函数时,它不会抱怨,但它不再检查 arglist - 它在一个文件中进行。 CCL / CLISP 仍然检查 arglists。
    【解决方案2】:

    如果函数在同一个文件中,编译器不会给出警告。

    SBCL 示例:

    bash-3.2$ sbcl
    This is SBCL 1.3.10, an implementation of ANSI Common Lisp.
    More information about SBCL is available at <http://www.sbcl.org/>.
    
    SBCL is free software, provided as is, with absolutely no warranty.
    It is mostly in the public domain; some portions are provided under
    BSD-style licenses.  See the CREDITS and COPYING files in the
    distribution for more information.
    * (compile-file "/tmp/order.lisp")
    
    ; compiling file "/private/tmp/order.lisp" (written 28 NOV 2016 12:14:37 PM):
    ; compiling (DEFUN F ...)
    ; compiling (DEFUN F2 ...)
    
    ; /tmp/order.fasl written
    ; compilation finished in 0:00:00.178
    #P"/private/tmp/order.fasl"
    NIL
    NIL
    * (load *)
    
    T
    * 
    

    【讨论】:

      【解决方案3】:

      您不必将函数放入 Common Lisp 中的同一个文件中,以便它们位于同一个编译单元中。

      这样做是一种反模式;当然,大型程序是由模块构成的,其中大多数调用另一个模块中的函数。您不能将整个程序滚动到单个物理模块中以避免出现警告。

      Lisp 有一种机制,可以将一组编译器视为一个编译单元:with-compilation-unit 宏:

      (with-compilation-unit
        (compile-file "file-f")
        (compile-file "file-f2"))
      

      如果您使用 ASDF 构建系统,我似乎记得它在后台为您执行 with-compilation-unit,围绕系统的所有文件。

      这种方法将有助于消除那些被延迟的警告。也就是说,如果实现警告未定义的标识符,但 defers 这样做直到编译单元结束,那么如果使用此宏,则延迟会延长到总编译结束单元跨越多个文件。

      当有关未定义标识符的警告被延迟时,目的是消除这些警告。如果先前未定义的函数的定义出现在翻译单元的结尾之前,则可以抑制警告。此宏允许一个文件中的定义抑制另一个文件中的延迟警告。

      如果实现不延迟警告,则宏将无济于事。

      【讨论】:

      • 这只是将警告推迟到编译单元的末尾。问题是摆脱警告。
      • 如果你想一想,似乎通过在两个文件的编译周围包装一个 WITH-COMPILATION-UNIT 来消除警告也不是一个好主意。该函数在第一个文件中仍然是未定义的。
      • @RainerJoswig 我不知道用 ANSI CL 方式说“请摆脱这个警告”。这个想法是,如果实现延迟警告,那么with-compilation-unit 会将延迟扩展到多个文件。实现延迟警告正是为了消除不必要的警告。如果在编译单元结束之前看到函数的定义,则延迟的“未定义函数”警告会自动消失。
      【解决方案4】:

      只需更改您的 defun 的顺序。首先,定义 f2 和大于 f。

      【讨论】:

      • 如果你有相互递归会在调用图中创建一个循环,则不能使用这种方法。
      • 虽然,定义然后使用是一个很好的程序组织。但是,如果不移动函数,很难在大型程序中长时间维护,这会产生干扰合并的难看的文本差异。经常你会发现,“该死,我现在需要从这里调用这个函数,但它是稍后定义的”。
      猜你喜欢
      • 2011-11-28
      • 1970-01-01
      • 2022-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多