如何在我的控制栈中解释这个栈帧?

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,即全局环境。