使用“ 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的顺序排列-无论先匹配什么,都是降压的地方。