【发布时间】:2012-05-27 07:52:03
【问题描述】:
在阅读了大量有关 Lisp eval-when 运算符的文档后,我仍然无法理解它的用途,我知道使用此运算符我可以控制表达式的评估时间,但我无法找出任何可能的示例适用吗?
最好的问候, utxeee。
【问题讨论】:
标签: compilation macros lisp common-lisp eval-when
在阅读了大量有关 Lisp eval-when 运算符的文档后,我仍然无法理解它的用途,我知道使用此运算符我可以控制表达式的评估时间,但我无法找出任何可能的示例适用吗?
最好的问候, utxeee。
【问题讨论】:
标签: compilation macros lisp common-lisp eval-when
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 的定义。因此,在以后文件编译器需要在BAZ的使用宏扩展过程中调用BAR时可用。
只能使用:compile-toplevel,当文件编译后不需要该功能时。如果稍后使用它,那么我们需要确保它被加载。
所以EVAL-WHEN 允许指定是否应该运行一段特定的代码
EVAL-WHEN 在用户代码中并不经常使用。如果你使用它,那么你应该问问自己是否真的需要它。
【讨论】:
load-top-level ==> :load-toplevel?我不得不深入研究源代码才能找到