也许这有点菜鸟,但我在这之后失去了理智。我非常惊讶地发现CL的stepper并没有显示各种形式的返回值。我的意思是调试器可以检查帧,并且 REPL 允许手动检查变量,但例如在类似的代码中
(defun fact (n)
(if (<= n 0)
1
(* n (fact (- n 1)))))
(fact 5)
我无法指示调试器说“嘿,我刚刚跨过表单 (<= n 0)
,它返回 nil
,然后我跨过 (- n 1)
,它返回值 4
”。这可以在 Emacs-lisp 和 Clojure 中完成(我使用 Emacs,但它不是我感兴趣的界面)。我知道存在 (step)
形式,但它依赖于实现,并且就我在 SBCL 和 CMU 上看到的内容而言,它并没有达到我的意思。所以我的问题是,CL 的调试器可以做到这一点(步进+刚刚评估的打印值)吗?如果是,它只是一种实现吗?您能提供 MWE 吗?
谢谢!
最佳答案
Common Lisp 是一种语言规范,而不是一种实现。因此,诸如“CL 的步进器”或“CL 的调试器”之类的表达式属于类别错误:语言没有步进器或调试器,但实现有。语言规范提供了一个接口(interface),step
,这样的事情可能会被调用,但是说
step
implements a debugging paradigm wherein the programmer is allowed to step through the evaluation of a form. The specific nature of the interaction, including which I/O streams are used and whether the stepping has lexical or dynamic scope, is implementation-defined.
因此,您的问题的答案是不,“CL 的调试器”无法执行此操作,因为“CL 的调试器”不存在。
该语言的实现可能会为此提供或多或少的支持。例如,我知道 LispWorks例如,确实如此。我不知道其他实现是否如此。 CL 作为一种语言也具有足够的反射性,可以编写可移植步进器:sly-stepper可能是这样的事情,但我不确定。
以下是使用 LW 非 IDE 步进器的示例:
CL-USER 7 > (step (let ((x 0)) (/ (sin x) x)))
(let ((x 0)) (/ (sin x) x)) -> :su
Error: Division-by-zero caused by / of (0.0 0.0).
1 (continue) Return a value to use.
2 Supply new arguments to use.
3 (abort) Return to debug level 1.
4 Return to stepper level 1.
5 Quit from stepper.
6 Return to debug level 0.
7 Restart top-level loop.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 9 : 2 > :a
(let ((x 0)) (/ (sin x) x)) -> :sr 0.0
0.0
0.0
CL-USER 8 > (step (let ((x 0)) (/ (sin x) x)))
(let ((x 0)) (/ (sin x) x)) -> :s 2
0
0
(/ (sin x) x)
(sin x) -> :si
0.0
x -> :sr 1
1
0.0
0.0
0.0
在第一次交互中,我让它运行,直到出现错误,从中中止回到步进器,然后告诉步进器返回零。在第二步中,我单步执行,直到它即将计算 x
,然后告诉它结果是 1
,导致它返回零。
关于debugging - 如何单步执行一些 Common Lisp 代码,检查表单的返回值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76231452/