Eval-何时使用?

utx*_*eee 12 lisp macros compilation common-lisp eval-when

在阅读了很多关于Lisp eval-when运算符的文档后,我仍然无法理解它的用法,我知道使用这个运算符我可以控制表达式的评估时间,但是我无法弄清楚这可能适用的任何例子?

最诚挚的问候,utxeee.

Rai*_*wig 23

编译Lisp文件

以编译Lisp文件为例.Lisp编译器处理顶级表单.这些可以是任意的Lisp格式,DEFUN,DEFMACROS,DEFCLASS,函数调用,......

文件编译器工作原理的全部内容过于复杂,无法在此解释,但有以下几点:

  • 文件编译器为(DEFUN foo () )表单生成代码.但它没有执行defun形式.因此,在编译期间,已知存在函数FOO,但是在编译期间'FOO'的代码不可用.编译器为编译的文件生成代码,但不将其保留在内存中.你不能在编译时调用这样的函数.

  • 对于宏,这个工作略有不同:(DEFMACRO BAZ ...).文件编译器不仅会编译宏并注意它在那里,但它也会在编译时使宏可用.它被加载到编译环境中.

因此想象一下文件中的表单序列:

(defmacro baz ...)

(defun foo () (baz ...))
Run Code Online (Sandbox Code Playgroud)

这是因为文件编译器知道宏BAZ并且在编译代码时FOO,它可以扩展宏窗体.

现在让我们看看以下示例:

(defun bar (form) ...)

(defmacro baz (form) (bar form))

(defun foo () (baz ...))
Run Code Online (Sandbox Code Playgroud)

以上不起作用.现在宏通过调用它来BAZ使用该函数BAR.当编译器尝试编译该函数时FOO,它无法扩展BAZ宏,因为BAR无法调用,因为代码BAR未加载到编译时环境中.

有两种解决方案:

  1. 使用单独的文件进行编译加载BAR.
  2. 使用EVAL-WHEN

示例EVAL-WHEN:

 (eval-when (:compile-toplevel :execute :load-toplevel)
   (defun bar (form) ...)
 )

 (defmacro baz (form) (bar form))

 (defun foo () (baz ...))
Run Code Online (Sandbox Code Playgroud)

现在EVAL-WHEN指示文件编译器在编译期间实际运行DEFUN表单.这样做的结果是:该文件编译器现在知道的定义,BAR编译时.因此它在以后可用,当文件编译器需要BAR在宏扩展期间调用时使用BAZ.

只有:compile-toplevel在编译文件后不需要该函数时才可以使用.如果以后使用它,那么我们需要确保它被加载.

因此,EVAL-WHEN允许指定是否应运行特定代码段

  • 在编译文件期间
  • 在加载文件期间
  • 执行期间

EVAL-WHEN通常在用户代码中不使用.如果你使用它,那么你应该问自己是否真的需要它.

  • 你能改正错字 `load-top-level` ==> `:load-toplevel` 吗?我不得不深入研究来源才能找到 (2认同)