在阅读了很多关于Lisp eval-when
运算符的文档后,我仍然无法理解它的用法,我知道使用这个运算符我可以控制表达式的评估时间,但我无法弄清楚任何一个例子这可能适用吗?
最诚挚的问候, utxeee。
答案 0 :(得分:24)
汇编Lisp文件
以编译Lisp文件为例。 Lisp编译器处理顶级表单。这些可以是任意的Lisp格式,DEFUN,DEFMACROS,DEFCLASS,函数调用,......
文件编译器工作原理的全部内容过于复杂,无法在此解释,但有一些事情:
文件编译器为(DEFUN foo () )
表单生成代码。但它没有执行defun形式。因此,在编译期间,已知存在函数FOO
,但在编译期间'FOO'的代码不可用。编译器为编译的文件生成代码,但不将其保留在内存中。你不能在编译时调用这样的函数。
对于宏,它的工作方式略有不同:(DEFMACRO BAZ ...)
。文件编译器不仅会编译宏并注意它在那里,但它也会在编译时使宏可用。它被加载到编译器 environment 。
因此想象一下文件中的表单序列:
(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
的定义。因此,当文件编译器需要在使用BAR
的宏扩展期间调用BAZ
时,它可用。
在编译文件后不需要该函数时,只能使用:compile-toplevel
。如果以后使用它,那么我们需要确保它被加载。
所以EVAL-WHEN
允许指定是否应该运行特定的代码
EVAL-WHEN
。如果你使用它,那么你应该问问自己是否真的需要它。