Common lisp 在CommonLisp中,何时需要使用eval何时以及如何知道?
当时,必须使用Common lisp 在CommonLisp中,何时需要使用eval何时以及如何知道?,common-lisp,compile-time,eval-when,Common Lisp,Compile Time,Eval When,当时,必须使用eval,以确保宏所依赖的函数在编译和使用宏时可用。但是,我想不出一个例子来说明在时不使用eval的后果 (defpackage :eval-when (:use :cl)) (in-package :eval-when) (defun util-fun (x) (* x x)) (defmacro needs-help (x) `(let ((a (util-fun ,x))) a)) ;; use it in the same file (defun use-th
eval,以确保宏所依赖的函数在编译和使用宏时可用。但是,我想不出一个例子来说明在
时不使用eval的后果
(defpackage :eval-when
(:use :cl))
(in-package :eval-when)
(defun util-fun (x) (* x x))
(defmacro needs-help (x) `(let ((a (util-fun ,x))) a))
;; use it in the same file
(defun use-the-macro (x) (needs-help x))
(use-the-macro 5)
如果我理解正确,则(defun util fun…
应该在时用eval包装
(defpackage :eval-when
(:use :cl))
(in-package :eval-when)
(defun util-fun (x) (* x x))
(defmacro needs-help (x) `(let ((a (util-fun ,x))) a))
;; use it in the same file
(defun use-the-macro (x) (needs-help x))
(use-the-macro 5)
EDIT:从答案中可以看出,这个示例存在一个问题:它实际上在编译时不调用UTIL-FUN。这解释了为什么没有给出错误,因为它不是错误。但这个问题仍然有效,因为它突出了新用户的困惑
但是,从REPL来看,在编译、加载或使用过程中没有发出错误或警告(SBCL 1.3.20):
;黏液2.19
CL-USER>(uiop:getcwd)
#P“/home/anticrisis/dev/common lisp/eval when/”
CL-USER>(编译文件“eval when.lisp”)
; 编译文件“/home/anticrisis/dev/common-lisp/eval-when/eval-when.lisp”(写于2017年8月14日上午11:30:49):
; 编译(DEFPACKAGE:EVAL-WHEN…)
; 编译(包内:EVAL-WHEN)
; 编译(DEFUN UTIL-FUN…)
; 编译(DEFMACRO需要帮助…)
; 编译(定义使用宏…)
; 编译(使用宏5)
; /home/anticrisis/dev/common lisp/eval-when/eval-when.fasl已写入
; 编译在0:00:00.009完成
#P“/home/anticrisis/dev/common-lisp/eval-when/eval-when.fasl”
无
无
CL-USER>(包中:评估时间)
#
EVAL-WHEN>(使用宏3)
; 对#的评估已中止。
EVAL-WHEN>(需要帮助4)
; 对#的评估已中止。
EVAL-WHEN>(加载“EVAL-WHEN.lisp”)
T
EVAL-WHEN>(使用宏3)
9
EVAL-WHEN>(需要帮助4)
16
EVAL-WHEN>
请注意,通常我使用C-C-k来计算并将文件加载到repl,但在这里,我使用编译文件
和加载
命令来证明没有错误发生。(当我在编译函数之后但在加载函数之前尝试使用这些函数时,确实会收到一个错误,但任何卸载的代码都会出现这种情况。)
之前有与此相关的问题和意见:
- 这似乎非常清楚地表明,宏使用的任何函数在
窗体时都必须包含在eval中,或者加载到单独的文件中
coredump的评论也非常清楚:
展开宏时,宏调用的任何函数都必须
定义如果您有一个定义宏的编译单元
调用函数,但实际上不在同一个函数中使用宏
编译单元,在编译时不需要eval。但是,如果您定义了
辅助的。函数,一个宏,并希望在完成后立即使用您的宏
定义它,那么实现可能会抱怨aux。
函数未知–coredump
既然如此,为什么我的示例不生成错误?我的例子在其他情况下会失败吗?举例说明在
时未能正确使用eval时生成的编译时、加载时或运行时错误有助于我的理解
谢谢你的耐心 记住
EVAL-WHEN用于告诉文件编译器是否应该在编译时执行代码(函数定义通常不这样做),以及是否应该在加载时安排编译文件中的编译代码执行。这仅适用于顶级表单
Common Lisp在完整的Lisp环境中运行文件编译器(请记住,我们谈论的是编译文件,而不是在REPL中执行),并且可以在编译时运行任意代码(例如,作为开发环境工具的一部分,生成代码、优化代码等)。如果文件编译器想要运行代码,那么文件编译器需要知道定义
还要记住,在宏扩展期间,宏的代码会被执行以生成扩展的代码。宏本身为计算代码而调用的所有函数和宏都需要在编译时可用。编译时不需要的是宏表单扩展到的代码
这有时会造成混乱,但可以学习,然后使用它并不难。但令人困惑的是,文件编译器本身是可编程的,可以在编译时运行Lisp代码。因此,我们需要理解代码可能在不同情况下运行的概念:在REPL中、在加载时、在编译时、在宏扩展期间、在运行时,等等
还要记住,编译文件时,如果编译器以后需要调用部分文件,则需要加载该文件。如果函数刚刚编译,文件编译器将不将代码存储在编译时环境中,也不会在完成文件编译后存储。如果需要执行代码,则需要加载编译后的代码->或使用EVAL-WHEN->见下文
您的代码
您的代码不会在编译时调用函数util fun
。因此,函数不需要在编译时环境中可用
示例
另一个实际调用函数的示例,请参见下文。这是Lisp文件中的代码,由compilefile
编译
(defun run-at-compile-time ()
(print 'I-am-called-at-compile-time))
(defmacro foo ()
(run-at-compile-time) ; this function is called for its
; side-effect: it prints something
'(print 'I-am-called-at-runtime)) ; this code is returned
(foo) ; we use the macro in our code, the compiler needs to expand it.
因此,在宏扩展期间,宏foo
喜欢调用函数run at compile time
,该函数在同一文件中定义。由于它在编译时环境中不可用,因此这是一个错误。文件编译器只为要存储在磁盘上的函数生成代码,这样在加载编译后的文件时,就可以定义函数。但是它没有在运行编译器的Lisp中定义函数->文件编译器无法调用它
引入EVAL-WHEN(eval-when
(:compile-toplevel ; this top-level form will be executed by the file compiler
:load-toplevel ; this top-level form will be executed at load-time
; of the compiled file
:execute) ; executed whenever else
(defun run-at-compile-time ()
(print 'I-am-called-at-compile-time))
)