在阅读了大量有关 Lispeval-when
运算符的文档后,我仍然无法理解它的用途,我知道使用此运算符我可以控制表达式的评估时间,但我无法找出任何适用的示例?
最好的问候,utxeee。
在阅读了大量有关 Lispeval-when
运算符的文档后,我仍然无法理解它的用途,我知道使用此运算符我可以控制表达式的评估时间,但我无法找出任何适用的示例?
最好的问候,utxeee。
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
没有加载到编译时环境中。
有两种解决方案:
BAR
示例EVAL-WHEN
:
(eval-when (:compile-toplevel :execute :load-toplevel)
(defun bar (form) ...)
)
(defmacro baz (form) (bar form))
(defun foo () (baz ...))
现在EVAL-WHEN
指示文件编译器在编译期间实际运行 DEFUN 表单。这样做的效果是:文件编译器现在知道BAR
at compile time的定义。因此,当文件编译器需要BAR
在宏扩展期间调用BAZ
.
:compile-toplevel
当编译文件后不需要该功能时,可以使用 only 。如果稍后使用它,那么我们需要确保它被加载。
所以EVAL-WHEN
允许指定是否应该运行一段特定的代码
EVAL-WHEN
在用户代码中不经常使用。如果你使用它,那么你应该问问自己是否真的需要它。