如何像这样介入SBCL?

How to step in SBCL like this?

我是 Common Lisp 的新手,我正在使用 SBCL、Slime 和 Emacs 来学习。

在阅读Common Lisp: A Gentle Introduction to Symbolic Computation一书时,作者提到了有助于调试并能够做到这一点的STEP工具:

斜体文字是来自作者还是工具本身,还不是100%清楚。可能,只是作者的评论。

然而,即使我不考虑斜体,我无法生成这样的描述信息。

如果我只使用 SBCL 的 REPL,我得到:

* (step (if (oddp 5) 'yes 'no))            
YES

如果我在启用 Slime 的情况下在 Emacs 中使用 REPL,我得到:

CL-USER> (step (if (oddp 5) 'yes 'no))
YES

作者说:

Each implementation of Common Lisp provides its own version of this tool; only the name has been standardized.

如果我在 Emacs/Slime 中用一个函数尝试同样的事情,我会得到更多信息:

(defun my-abs (x)
  (cond ((> x 0) x)
    ((< x 0) (- x))
    (t 0)))

在 REPL 中使用上面的定义和下面的命令:

CL-USER> (step (my-abs 10))

我得到:

Evaluating call:
  (MY-ABS 10)
With arguments:
  10
   [Condition of type STEP-FORM-CONDITION]

Restarts:
 0: [STEP-CONTINUE] Resume normal execution
 1: [STEP-OUT] Resume stepping after returning from this function
 2: [STEP-NEXT] Step over call
 3: [STEP-INTO] Step into call
 4: [RETRY] Retry SLIME REPL evaluation request.
 5: [*ABORT] Return to SLIME's top level.
 --more--

Backtrace:
  0: ((LAMBDA ()))
  1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LET ((SB-IMPL::*STEP-OUT* :MAYBE)) (UNWIND-PROTECT (SB-IMPL::WITH-STEPPING-ENABLED #))) #S(SB-KERNEL:LEXENV :FUNS NIL :VARS NIL :BLOCKS NIL :TAGS NIL :TYPE-RESTRICTIONS ..
  2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (STEP (MY-ABS 10)) #<NULL-LEXENV>)
  3: (EVAL (STEP (MY-ABS 10)))
 --more--

不幸的是,none 这些选项似乎给了我想要的东西(这可能是我这边的解释错误)。

我想看类似的内容:

SLIME 似乎是一个彻底的工具。我可能遗漏了什么。

有没有办法使用 SLIME 或 SBCL 生成书中描述的相同输出?

正如上面有人建议的那样,SBCL 默认情况下确实进行了很多优化,默认情况下也会编译。这是我创建示例所做的工作:

  • 我先给运行ning
  • 的“建议优化”补了个“不好的值”
(declaim (optimize (debug 3) (space 0) (speed 0)))
  • 然后,我用 定义了一个函数,而不是 if 条件 ,因为这种东西总是内联的,不幸的是(虽然你可以尝试 (declaim (notinline ...)),我没有。一种方法是创建一个 调用另一个函数的函数,例如:
(defun foo () "hey!")

(defun bar () (foo))
  • 现在,当我 运行 (step (bar)) 时,我看到了您在上面的问题中共享的调试器窗格,如果我现在 select 选项 #3,step into,我得到了相同的窗格,但现在正如所希望的那样,专注于对 foo.
  • 的调用

祝你好运!

一些参考资料:

看来作者使用的是 LispWorks 的步进器。

使用 LispWorks

这是我的步进会话,使用 :s 步进当前表单及其所有子表单。

CL-USER 5 > (step (my-abs -5))
(MY-ABS -5) -> :s
   -5 -> :s
   -5 
   (COND ((> X 0) X) ((< X 0) (- X)) (T 0)) <=> (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0)))
   (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0))) -> :s
      (> X 0) -> :s
         X -> :s
         -5 
         0 -> :s
         0 
      NIL 
      (IF (< X 0) (- X) (PROGN 0)) -> :s
         (< X 0) -> :s
            X -> :s
            -5 
            0 -> :s
            0 
         T 
         (- X) -> :s
            X -> :s
            -5 
         5 
      5 
   5 
5 
5

正在提供帮助 :?:

 :?

:s       Step this form and all of its subforms (optional +ve integer arg)
:st      Step this form without stepping its subforms
:si      Step this form without stepping its arguments if it is a function call
:su      Step up out of this form without stepping its subforms
:sr      Return a value to use for this form
:sq      Quit from the current stepper level
:bug-form <subject> &key <filename>
         Print out a bug report form, optionally to a file.
:get <variable> <command identifier>
         Get a previous command (found by its number or a symbol/subform within it) and put it in a variable.
:help    Produce this list.
:his &optional <n1> <n2>
         List the command history, optionally the last n1 or range n1 to n2.
:redo &optional <command identifier> 
         Redo a previous command, found by its number or a symbol/subform within it.
:use <new> <old> &optional <command identifier> 
         Do variant of a previous command, replacing old symbol/subform with new symbol/subform.

对于编译后的代码,它还有一个可视化步进器,您可以在其中按红色按钮设置断点,查看中间变量的变化等。它看起来像这样:

LispWorks 是专有实现,IDE 有免费但受限的版本。我刚刚写了 a review 应该合并到 Cookbook 上。

跟踪和打印

你知道trace吗? printv,一个外部图书馆,是类固醇的痕迹。它们类似于您欣赏的输出。

(defun factorial (n)
  (if (plusp n)
    (* n (factorial (1- n)))
    1))
(trace factorial)

(factorial 2)
  0: (FACTORIAL 3)
    1: (FACTORIAL 2)
      2: (FACTORIAL 1)
        3: (FACTORIAL 0)
        3: FACTORIAL returned 1
      2: FACTORIAL returned 1
    1: FACTORIAL returned 2
  0: FACTORIAL returned 6
6

(untrace factorial)

printv 打印代码和返回值。

(printv:printv
           (+ 2 3)              
           *print-case*
           *package*
           'symbol
           (let* ((x 0) (y (1+ x)) (z (1+ y)))
             (values x y z)))
;;;   (+ 2 3) => 5
;;;   *PRINT-CASE* => :UPCASE
;;;   *PACKAGE* => #<PACKAGE "ISSR-TEST">
;;;   'SYMBOL => SYMBOL
;;;   (LET* ((X 0) (Y (1+ X)) (Z (1+ Y)))
        (VALUES X Y Z)) =>
           [ [X=0]  [Y=1]  [Z=2] ]
;;;   => 0, 1, 2