首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在常见的Lisp中,什么时候需要使用,以及如何知道?

在常见的Lisp中,什么时候需要使用,以及如何知道?
EN

Stack Overflow用户
提问于 2017-08-14 00:08:09
回答 1查看 3.4K关注 0票数 15

eval-when的一个必要用途是确保宏所依赖的函数在编译和使用宏时可用。但是,我想不出一个例子来演示不使用eval-when的后果。

代码语言:javascript
复制
(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-when包装。

编辑:您将从答案中看到,这个示例有一个问题:它实际上在编译时不调用UTIL-。这解释了为什么没有错误,因为它不是一个错误。但是这个问题仍然有效,因为它突出了一个新用户的困惑。

但是,在编译、加载或使用期间,REPL没有发出错误或警告(SBCL 1.3.20):

代码语言:javascript
复制
; SLIME 2.19
CL-USER> (uiop:getcwd)
#P"/home/anticrisis/dev/common-lisp/eval-when/"
CL-USER> (compile-file "eval-when.lisp")
; compiling file "/home/anticrisis/dev/common-lisp/eval-when/eval-when.lisp" (written 14 AUG 2017 11:30:49 AM):
; compiling (DEFPACKAGE :EVAL-WHEN ...)
; compiling (IN-PACKAGE :EVAL-WHEN)
; compiling (DEFUN UTIL-FUN ...)
; compiling (DEFMACRO NEEDS-HELP ...)
; compiling (DEFUN USE-THE-MACRO ...)
; compiling (USE-THE-MACRO 5)

; /home/anticrisis/dev/common-lisp/eval-when/eval-when.fasl written
; compilation finished in 0:00:00.009
#P"/home/anticrisis/dev/common-lisp/eval-when/eval-when.fasl"
NIL
NIL
CL-USER> (in-package :eval-when)
#<PACKAGE "EVAL-WHEN">
EVAL-WHEN> (use-the-macro 3)
; Evaluation aborted on #<UNDEFINED-FUNCTION USE-THE-MACRO {10035E1103}>.
EVAL-WHEN> (needs-help 4)
; Evaluation aborted on #<UNDEFINED-FUNCTION UTIL-FUN {100387FE33}>.
EVAL-WHEN> (load "eval-when.lisp")
T
EVAL-WHEN> (use-the-macro 3)
9
EVAL-WHEN> (needs-help 4)
16
EVAL-WHEN> 

注意,通常我使用occurs来评估并将一个文件加载到repl,但是在这里,我使用compile-fileload命令来演示没有发生错误。(在编译函数之后,在加载函数之前,尝试使用这些函数时,确实会收到错误,但是在任何卸载的代码中都会出现错误。)

以前有一些与此有关的问题和评论:

  • 这个previous StackOverflow answer似乎非常明确地说,宏使用的任何函数都必须由eval-when表单封装,或者加载到单独的文件中。
  • coredump的这一评论也非常明确: 展开宏时,必须定义宏调用的任何函数。如果您有一个编译单元,它定义了一个宏,它调用函数,但是您实际上没有在同一个编译单元中使用这个宏,那么您不需要functions。但是,如果您定义了辅警。函数,并希望在定义宏之后立即使用您的宏,则实现可能会抱怨aux。函数是未知的

既然如此,为什么我的例子不产生错误呢?在其他情况下,我的例子会失败吗?在没有正确使用eval-when时生成编译时、加载时或运行时错误的示例将有助于我的理解。

感谢您的耐心!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-08-14 22:07:37

记住

EVAL-WHEN是为了告诉文件编译器是否应该在编译时执行代码(例如,函数定义通常不执行),以及是否应该安排编译文件中的编译代码在加载时执行。这只适用于顶级表单。

通用Lisp在完整的Lisp环境中运行文件编译器(请记住,我们正在讨论编译文件,而不是在REPL中执行),并且可以在编译时运行任意代码(例如,作为开发环境工具的一部分,生成代码,优化代码等等)。如果文件编译器想要运行代码,那么文件编译器就需要知道定义。

还请记住,在宏展开期间,将执行宏的代码以生成展开的代码。宏本身为计算代码而调用的所有函数和宏都需要在编译时可用。在编译时不需要可用的是宏表单扩展到的代码。

这有时是一个混乱的根源,但它可以学习,然后使用它并不是太难。但令人困惑的是,文件编译器本身是可编程的,可以在编译时运行Lisp代码。因此,我们需要理解代码可能在不同情况下运行的概念:在REPL中,在加载时,在编译时,在宏展开期间,在运行时,等等。

还请记住,当您编译一个文件时,如果编译器需要稍后调用它的部分,那么您需要加载该文件。如果只是编译了一个函数,那么文件编译器将 not 存储在编译时环境中,也不会在完成文件编译之后存储代码。如果需要执行代码,则需要加载编译后的代码->或使用EVAL-WHEN ->,参见下面。

您的代码

您的代码在编译时不调用函数util-fun。因此,函数而不是需要在编译时环境中可用。

示例

另一个例子,函数实际上被调用,请参见下面。这是由compile-file编译的Lisp文件中的代码。

代码语言:javascript
复制
(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.
            ; Thus during macro expansion the function
            ; RUN-AT-COMPILE-TIME will be called.

因此,在宏展开期间,宏foo喜欢调用函数run-at-compile-time,该函数是在同一个文件中定义的。因为它在编译时环境中不可用,所以这是一个错误。文件编译器只生成要存储在磁盘上的函数的代码,这样当加载编译后的文件时,函数就会被定义。但是它没有定义在Lisp内部运行编译器->的函数,文件编译器不能调用它。

EVAL-WHEN 引入

为了让编译器也让编译时环境知道它,您需要将它包装在一个EVAL-WHEN中,并添加:compile-toplevel情况。然后,当文件编译器在toplevel上看到函数时,它运行定义的宏。

代码语言:javascript
复制
(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))

 )

你也可以只提到一两种情况。例如,当文件编译器在顶层看到表单时,只有在那时才能执行该表单。它不会在加载时或其他情况下执行。

票数 23
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45665951

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档