如果一个人总是需要这样子思考程序,递归会是艰难的、没有帮助的。递归的优点是它精确地让我们更抽象地来设计算法。你不需要考虑真正函数时所有的调用过程,就可以判断一个递归函数是否是正确的。

    要知道一个递归函数是否做它该做的事,你只需要问,它包含了所有的情况吗?举例来说,下面是一个寻找列表长度的递归函数:

    1. 对长度为 的列表是有效的。

    如果这两点是成立的,我们知道这个函数对于所有可能的列表都是正确的。

    我们的定义显然地满足第一点:如果列表( lst ) 是空的( nil ),函数直接返回 0 。现在假定我们的函数对长度为 n 的列表是有效的。我们给它一个 长度的列表。这个定义说明了,函数会返回列表的 cdr 的长度再加上 1cdr 是一个长度为 n 的列表。我们经由假定可知它的长度是 n 。所以整个列表的长度是 n+1

    更复杂的递归函数,可能会有更多的情况需要讨论,但是流程是一样的。举例来说, 41 页的 ,我们需要讨论三个情况: 原子,单一的 Cons 对象, n+1Cons 树。

    第一个情况(长度零的列表)称之为基本用例( base case )。当一个递归函数不像你想的那样工作时,通常是处理基本用例就错了。下面这个不正确的 member 定义,是一个常见的错误,整个忽略了基本用例:

    1. (defun our-member (obj lst)
    2. lst

    能够判断一个递归函数是否正确只不过是理解递归的上半场,下半场是能够写出一个做你想做的事情的递归函数。 6.9 节讨论了这个问题。