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未加载到编译时环境中.
有两种解决方案:
BAR.示例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通常在用户代码中不使用.如果你使用它,那么你应该问自己是否真的需要它.