15

在阅读了大量有关 Lispeval-when运算符的文档后,我仍然无法理解它的用途,我知道使用此运算符我可以控制表达式的评估时间,但我无法找出任何适用的示例?

最好的问候,utxeee。

4

1 回答 1

28

Lisp 文件的编译

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

文件编译器如何工作的整个故事太复杂了,无法在这里解释,但有几件事:

  • 文件编译器为(DEFUN foo () )表单生成代码。但它不执行 defun 形式。因此在编译过程中知道有一个函数FOO,但是在编译过程中没有ˋFOOˋ的代码。编译器为已编译文件生成代码,但不将其保存在内存中。您不能在编译时调用这样的函数。

  • 对于宏,这略有不同:(DEFMACRO BAZ ...). 文件编译器不仅会编译宏并注意它的存在,还会使宏在编译时可用。它被加载到编译器环境中

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

(defmacro baz ...)

(defun foo () (baz ...))

这是因为文件编译器知道宏BAZ,并且当它编译代码时FOO,它可以扩展宏形式。

现在让我们看下面的例子:

(defun bar (form) ...)

(defmacro baz (form) (bar form))

(defun foo () (baz ...))

以上将不起作用。现在宏通过调用它来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 ...))

现在EVAL-WHEN指示文件编译器在编译期间实际运行 DEFUN 表单。这样做的效果是:文件编译器现在知道BARat compile time的定义。因此,当文件编译器需要BAR在宏扩展期间调用BAZ.

:compile-toplevel当编译文件后不需要该功能时,可以使用 only 。如果稍后使用它,那么我们需要确保它被加载。

所以EVAL-WHEN允许指定是否应该运行一段特定的代码

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

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

于 2012-05-20T17:07:59.177 回答