gpt4 book ai didi

common-lisp - 在Common Lisp中,什么时候需要用到eval-when,又是怎么知道的呢?

转载 作者:行者123 更新时间:2023-12-05 00:14:55 26 4
gpt4 key购买 nike

必须使用 eval-when是为了确保宏所依赖的函数在编译和使用宏时可用。但是,我想不出一个例子来说明不使用 eval-when 的后果。 .

(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-FUN。这解释了为什么没有给出错误,因为它不是错误。但是这个问题仍然有效,因为它突出了新用户的困惑。

但是,从 REPL 中,在编译、加载或使用过程中不会发出错误或警告(SBCL 1.3.20):
; 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>

请注意,通常我使用 C-c C-k 来评估并将文件加载到 repl,但在这里,我使用的是 compile-fileload命令来证明没有发生错误。 (当我在编译之后但在加载之前尝试使用这些函数时,我确实收到了一个错误,但任何卸载的代码都会发生这种情况。)

有与此相关的先前问题和评论:
  • previous StackOverflow answer似乎很明确地说,宏使用的任何函数都必须用 eval-when 括起来。形式,或加载到单独的文件中。
  • 来自 coredump 的这个评论也很清楚:

    宏展开时,宏调用的任何函数都必须是
    定义。如果您有一个定义宏的编译单元,
    调用函数,但实际上并没有在同一个函数中使用宏
    编译单元,你不需要 eval-when。但是,如果您定义了一个
    辅助。功能,一个宏,并希望在您之后立即使用您的宏
    定义它,那么实现可能会提示 aux.
    功能未知 – coredump

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

    感谢您的耐心等待!

    最佳答案

    记住

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

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

    还要记住,在宏扩展期间,宏的代码被执行以生成扩展代码。宏的所有函数和宏本身计算代码的调用需要在编译时可用。不需要在编译时可用的是宏形式扩展到的代码。

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

    还要记住,当你编译一个文件时,你需要加载文件,如果编译器稍后需要调用它的一部分。如果一个函数刚被编译,文件编译器会不是 将代码存储在编译时环境中,而不是在完成文件编译之后。如果需要执行代码,则需要加载编译后的代码-> 或使用EVAL-WHEN -> 见下文。

    您的代码

    您的代码没有 调用 函数util-fun在编译时。所以函数做 不是 需要在编译时环境中可用。

    一个例子

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

    (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 情况。然后,当文件编译器在顶层看到该函数时,它会运行定义宏。
    (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))

    )

    您也可以仅提及其中一种或两种情况。例如,该表单可以在文件编译器在顶层看到它时才执行。它不会在加载时或其他情况下执行。

    关于common-lisp - 在Common Lisp中,什么时候需要用到eval-when,又是怎么知道的呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45665951/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com