问题描述
我正在尝试通过Common Lisp:符号计算的温和介绍这本书来学习 Common Lisp。此外,我正在使用 SBCL、Emacs 和 Slime。
在第 8 章的结尾,作者将调试器介绍为 lisp 编程的重要工具之一。然后,为了展示它,他在类似阶乘的函数定义中使用了 break
命令:
(defun fact-debugging (n)
(cond ((zerop n) (break "N is zero."))
(t (* n (fact-debugging (- n 1))))))
CL-USER> (fact-debugging 4)
我得到了控制堆栈。。我对回溯部分特别好奇:
N is zero.
[Condition of type SIMPLE-CONDITION]
Restarts:
0: [CONTINUE] Return from BREAK.
1: [RETRY] Retry SLIME REPL evaluation request.
2: [*ABORT] Return to SLIME's top level.
3: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {10024B9BC3}>)
Backtrace:
0: (FACT-DEBUGGING 1)
1: (FACT-DEBUGGING 2)
2: (FACT-DEBUGGING 3)
3: (FACT-DEBUGGING 4)
4: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FACT-DEBUGGING 4) #<NULL-LEXENV>)
5: (EVAL (FACT-DEBUGGING 4))
我可以理解除数字 4: (SB-INT...
之外的所有堆栈帧
有趣的是,作者并没有收到这样的消息。他得到了一些更轻的东西:
因此,我想问:
1 - 为什么会出现这个堆栈帧?
2 - 为什么它发生在 eval
之后和 (fact-debugging 4)
的堆栈帧之前?
3 - 它实际上是什么意思?它充满了诸如 LEXENV
或 #<NULL LEXENV>
之类的看不见的术语。
解决方法
当您在 REPL 中输入表单 (fact-debugging 4)
时,该表单使用 eval
进行评估,因此:5: (EVAL (FACT-DEBUGGING 4))
。
如果在5中将emacs指向EVAL
并按下M-.
(使用Slime),你会发现eval
正在调用eval-in-lexenv
,它本身也在调用simple-eval-in-lexenv
,因此:4: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FACT-DEBUGGING 4) #<NULL-LEXENV>)
。
如果您将点移动到第 4 帧并按 ENTER,您将看到如下内容:
4: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FACT-DEBUGGING 4) #<NULL-LEXENV>)
Locals:
SB-KERNEL:LEXENV = #<NULL-LEXENV>
SB-IMPL::ORIGINAL-EXP = (FACT-DEBUGGING 4)
将点移到显示 SB-KERNEL:LEXENV = #<NULL-LEXENV>
的行并按 ENTER 显示:
#<SB-KERNEL:LEXENV {1003FD12E3}>
--------------------
The object is a STRUCTURE-OBJECT of type SB-KERNEL:LEXENV.
因此,对 eval
的初始调用是由 REPL 进行的,而 eval
调用了 eval-in-lexenv
,后者调用了具有 simple-eval-in-lexenv
结构的 lexenv
。 lexenv
结构只是 eval
应在其中运行的词法环境的表示。您也可以将 emacs 指向 SB-KERNEL:LEXENV
以查看该定义。这个定义很长,但这是开始:
;;; The LEXENV represents the lexical environment used for IR1 conversion.
;;; (This is also what shows up as an ENVIRONMENT value in macroexpansion.)
(declaim (inline internal-make-lexenv))
(defstruct (lexenv
(:include abstract-lexenv)
;;;
;;; and so on....
;;;
一旦 eval
具有词法环境,就可以继续对 (FACT-DEBUGGING 4)
进行评估。当然,所有这些细节都是特定于 SBCL 的。在这种特殊情况下,词法环境 #<NULL-LEXENV>
是 null lexical environment,即全局环境。