如何避免这个编译错误?

Gus*_*ram 4 eval sbcl common-lisp

我有生成代码的代码(如say-hello),我发现使用生成的代码创建函数的最简单方法是将 a 放入类似这样的defun代码中eval

(defun say-hello ()
  `(princ "test"))

(eval
 `(defun test ()
    ,(say-hello)))

(test)
Run Code Online (Sandbox Code Playgroud)

该文件在命令行上运行良好:

sbcl --script test.lisp
Run Code Online (Sandbox Code Playgroud)

但是当我尝试在 SBCL 中编译它时会抱怨:

* (compile-file "test.lisp")

; compiling file "test.lisp" (written 19 APR 2018 01:05:19 PM):
; compiling (DEFUN SAY-HELLO ...)
; compiling (EVAL (SB-INT:QUASIQUOTE #))
; compiling (TEST)
; file: test.lisp
; in: TEST
;     (TEST)
;
; caught STYLE-WARNING:
;   undefined function: TEST
;
; compilation unit finished
;   Undefined function:
;     TEST
;   caught 1 STYLE-WARNING condition

; test.fasl written
; compilation finished in 0:00:00.031
#P"test.fasl"
T
NIL
*
Run Code Online (Sandbox Code Playgroud)

有没有办法编写代码来避免此编译错误?从生成代码的函数创建代码是否有更好的习惯用法?

Rai*_*wig 6

函数与宏 - 文件编译器

文件编译器对函数和宏定义的处理略有不同:

  • 函数:文件编译器编译它们并可以使用有关它们的信息。它将生成的代码转储到编译后的 fasl 文件中。但它不会在编译时环境中创建可执行定义。

  • 宏:文件编译器编译它们并使它们在编译时环境中可用。因此,编译器可以使用同一文件中较早定义的宏来扩展该宏在同一文件中的使用。

编译文件时表单的情况

  • :compile-toplevel -> 文件编译在编译文件并且表单位于顶层时对其进行评估

  • :load-toplevel -> 文件编译器生成代码,以便在加载期间执行顶级形式

你的代码

如果要在定义函数的同一文件中使用函数,则需要告诉编译器在编译时实际执行定义:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun fn-say-hello ()
    `(princ "test")))
Run Code Online (Sandbox Code Playgroud)

稍后在文件中宏可以使用该函数。编译器自动了解宏定义,但不了解函数。

(defmacro say-hello ()
  (fn-say-hello))  ; this is a function call, not returned code
Run Code Online (Sandbox Code Playgroud)

然后您可以在文件中使用该宏。然后编译器将扩​​展该宏并运行该函数fn-say-hello- 然后该函数就会起作用,因为我们之前告诉编译器该函数。

(defun test ()
  (say-hello))

(test)
Run Code Online (Sandbox Code Playgroud)