如何在我的控制栈中解释这个栈帧?
How to interpret this stack frame in my control stack?
我正在尝试通过 Common Lisp:对符号计算的简单介绍 来学习 Common Lisp。此外,我正在使用 SBCL、Emacs 和 Slime。
在第 8 章的末尾,作者介绍了调试器作为 lisp 编程的重要工具之一。然后,为了展示它,他在 factorial-like 函数定义中使用了 break
命令:
(defun fact-debugging (n)
(cond ((zerop n) (break "N is zero."))
(t (* n (fact-debugging (- n 1))))))
在 REPL 中调用函数后:
CL-USER> (fact-debugging 4)
我得到了控制栈。。我特别好奇 backtrace 部分:
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)
的 stack-frame 之前?
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
,后者调用了具有 lexenv
结构的 simple-eval-in-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,即全局环境。
我正在尝试通过 Common Lisp:对符号计算的简单介绍 来学习 Common Lisp。此外,我正在使用 SBCL、Emacs 和 Slime。
在第 8 章的末尾,作者介绍了调试器作为 lisp 编程的重要工具之一。然后,为了展示它,他在 factorial-like 函数定义中使用了 break
命令:
(defun fact-debugging (n)
(cond ((zerop n) (break "N is zero."))
(t (* n (fact-debugging (- n 1))))))
在 REPL 中调用函数后:
CL-USER> (fact-debugging 4)
我得到了控制栈。。我特别好奇 backtrace 部分:
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)
的 stack-frame 之前?
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
,后者调用了具有 lexenv
结构的 simple-eval-in-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,即全局环境。