3.函数的定义

  LISP程序设计,实际上就是定义函数。在基本函数和已经定义的函数的基础上,定义更多的函数,实现所需要的功能。

  我们已经介绍了许多LISP的基本函数,这些函数虽然包含了许多功能,但只能进行一些规定的操作,不能解决更多的问题。为此,LISP提供了DEFUN这个函数,允许用户自己定义所需要的函数,来解决用户自己的问题。
DEFUN的调用格式如下:
(DEFUN<函数名>(<形参表>)
{函数定义体})
例如我们想定义一个函数,它把表中没有的新元素加到表的前面。如果这个新元素已在原来的表中,就直接返回原来这个表。完成该功能的函数,我们把它起名为AUGMENT。其定义如下:
  定义函数AUGMENT,item和bag是该函数的两个参数。在函数定义中使用的参数,称为形参,如这里的iterm和bag都是形参。在实际调用函数时所用到的参数,称为实参。形参的值用实际调用该函数时的实参的值代替。因此,在定义一个函数时,可以想象形参的值就是实参的值。
(DEFUN AUGMENT (item bag)
(COND((MEMBER item bag) bag);已在原表中,返回原表。
(T(CONS item bag))));把item加到表的前面。
当这样定义好之后,我们可以象调用那些基本函数一样使用AUGMENT函数:
(AUGMENT 'a '(b c))==>(a b c)

  (1)函数的递归定义
  在LISP程序设计中,经常使用函数的递归定义。在第二章中,我们曾经介绍过递归的思想,对递归不熟悉的同学,在学习下面的内容之前,有必要先复习一下有关内容。

为了定义出更多、功能更强的函数,需要一些手段。LISP中最常用到的函数定义手段是递归。
所谓递归,就是函数自己调用自己。这就表示,至少有一个最简情况是可以解决的,另外,每递归一步都要使问题化简一步,并且化简的最终结果可以达到最简情况,否则将无穷递归下去。
下面我们通过例子来说明如何使用递归。最简单的例子就是数学函数本身的定义就是递归的。

这是一个阶乘函数的例子。通过这个大家所熟悉的例子,可以比较好的理解函数的递归定义。

例如,阶乘函数的数学定义为:
0!=1
1!=1
n!=(n-1)!n
根据此定义,我们可以很容易地写出其LISP定义。
定义阶乘函数,函数名为N!,参数为n。
(DEFUN N!(n))
当n等于0时,阶乘为1。
(COND((=n 0)1);n等于0时为1
当n等于1时,阶乘为1。以上定义了两个最简情况。
((=n 1)1);n等于1时为1
其他情况下,n的阶乘是n乘以n-1的阶乘。其中n-1的阶乘通过递归计算求得。
(T(*n(N!(- n 1)))));递归

图6.1形象的说明了当输入参数为4时(即求4的阶乘)该阶乘函数是如何递归计算的。
要求4的阶乘,函数将其转化为求3的阶乘(在○2的上部向下的箭头左边表示的数字3,表示第二次调用阶乘函数,参数为3。下同),而3的阶乘又被转化为求2的阶乘,2的阶乘再次被转化为求1的阶乘。1的阶乘是一个最简情况,其阶乘返回值为1(在○4的上部向上的箭头右边表示的数字1,表示第四次调用阶乘函数,返回值为1。下同)。有了1的阶乘,由定义知2的阶乘等于1的阶乘乘以2,所以得到2的阶乘为2。同理,得到3的阶乘为6和4的阶乘为24。最后,阶乘函数返回值为24,结束计算。

当对(N! 4)进行求值时,其递归过程可以用图6.1来表示。图中的每一个"О"表示函数的一次调用,"О"中的数字表示调用的先后次序,向下的箭头进入圆圈,表示调用函数,该箭头左方的数字为这次调用的函数的参量值。从圆圈向上发出的箭头表示函数此次调用的返回值,其值标在箭头的右方。