使用“ printf调试”
您可以让Emacs通过修改函数定义来帮助您理解:
(defun triangle-using-cond (number)
(message (format "called with %d" number))
(cond ((<= number 0) 0)
((= number 1) 1)
((> number 1)
(+ number (triangle-using-cond (1- number))))))
只需在(message ...)
某处添加一条线索即可将其打印到*Messages*
缓冲区。
使用Edebug
将点放置在函数定义内的任何位置,然后点击C-u C-M-x
“ 插入”它。然后评估功能,例如,将点放在之后(triangle-using-cond 3)
并点击C-x C-e
。
您现在处于Edebug模式。点击空格键以逐步完成该功能。每个表达式的中间值显示在回显区域中。要退出Edebug模式,只需点击q
。要删除检测,请将点放在定义内的任何位置,然后单击C-M-x
以重新评估定义。
使用标准的Emacs调试器
M-x debug-on-entry triangle-using-cond
,然后在triangle-using-cond
被调用时,您将被放置在Emacs调试器(buffer *Backtrace*
)中。
使用d
(或c
跳过任何无趣的评估)逐步进行评估。
要查看中间状态(变量值等),您可以e
随时使用。系统将提示您输入sexp进行评估,并打印评估结果。
使用调试器时,请在另一帧中保留源代码的副本,以便您可以跟踪发生的情况。
您还可以在源代码中的任意位置插入显式调用以进入调试器(或多或少的断点)。您插入(debug)
或(debug nil SOME-SEXP-TO-EVALUATE)
。在后一种情况下,将SOME-SEXP-TO-EVALUATE
评估进入调试器的时间并打印结果。(请记住,您可以将这样的代码插入源代码中并用于C-M-x
评估它,然后撤消-您无需保存已编辑的文件。)
有关Using Debugger
更多信息,请参见Elisp手册,节点。
递归循环
无论如何,将递归视为一个循环。定义了两种终止情况:(<= number 0)
和(= number 1)
。在这些情况下,函数将返回一个简单数字。
在递归的情况下,该函数将返回该数字的和与函数的结果number - 1
。最终,将使用1
小于或等于零的一个或一个数字来调用该函数。
因此,递归案例结果为:
(+ number (+ (1- number) (+ (1- (1- number)) ... 1)
举个例子(triangle-using-cond 4)
。让我们累积最终的表达式:
在第一个迭代中number
是4
,因此(> number 1)
跟随分支。我们开始建立一个表达式(+ 4 ...
,并调用与函数(1- 4)
,即(triangle-using-cond 3)
。
现在number
是3
,结果是(+ 3 (triangle-using-cond 2))
。总结果表达式为(+ 4 (+ 3 (triangle-using-cond 2)))
。
number
是2
现在,所以表达式是(+ 4 (+ 3 (+ 2 (triangle-using-cond 1))))
number
就是1
现在,我们采取(= number 1)
的分支,导致在一个无聊的1
。整个表达式是(+ 4 (+ 3 (+ 2 1)))
。评估是由内而外的,你会得到:(+ 4 (+ 3 3))
,(+ 4 6)
或只10
。
triangle-using-cond
上,参数要比数字小1。条件按a,b,c的顺序排列-无论先匹配什么,都是降压的地方。