1.函数的设计
函数是所有编程语言都会提供的基本语法结构,用户不仅可以调用编程语言内置的系统函数,也可以自定义函数。很多初学者对函数的使用存在困惑,主要体现在以下几个方面。
(1)不清楚什么时候需要定义函数。
(2)不确定函数是否需要参数,需要儿个参数,以及是否需要返回值
(3)不理解函数要处理的数据是哪些,不明白函数形参的作用是什么,形参的值是在什么时候被“赋予”的。初学者经常在函数内部通过输人语句给形参赋值
(4)不知道什么时候要调用自己定义的函数,不知道怎么确定函数的实参。
对于第(1)个问题,“函数”这个词的英文名称是“Function”,顾名思义,函数的作用就是实现某个具体的功能,通常只实现一个功能,而不会把多个功能糅合到一个函数里。通常,为了避免程序人口函数(如C/C+语言中的main函数)的代码过于庞大,我们需要把程序的功能分解,定义专门的函数来实现每个具体的功能。此外,如果某个功能被反复执行,为了避免这些功能代码反复出现,也需要定义函数来实现,每次执行该功能只需调用对应函数即可
对于第(2)个问题,程序设计者希望采用怎样的形式去调用函数,这种函数调用形式里有几个参数,分别是什么类型,以此来确定函数的形参个数和类型;程序设计者是否希望函数执行以后得到一个结果,这个结果是什么类型的,是什么含义,这个结果是否需要返回到主调函数中,以此来确定函数的返回值及其类型、含义等
对于第(3)个问题,函数形参是在函数调用时,通过实参与形参之间的数据传递,从而被赋予了值。只要没有函数调用发生,就不会给形参分配存储空间,所以定义函数时的参数才称为形式参数,简称“形参”。当函数调用发生时,为形参分配存储空间,并把实参的值传递给形参。所以,函数形参的作用是接收传递过来的实参的值。
不同编程语言,实参和形参之间传递数据的方式有差异。对C/C+语言,不管参数是普通数据类型还是指针类型,实参和形参之间传递数据的方式都是“值的传递”,简单地说,就是将实参的值赋给形参。在C语言里,形参还可以引用,调用这样的函数时,实参和形参是同一个变量。
对于第(4)个问题,求解问题时如果需要执行设计函数时确定的功能,就需要调用函数。由于函数形参的值是由实参传递过去的,因此,实参的值其实就是执行该函数时形参的初始值。
2.递归函数的设计和调用
(1)理解递归函数。理解递归函数时需要注意以下问题
a.递归函数的调用和执行过程。普通函数的调用通常只有一两层,但递归函数的调用可能有很多层
b.递归函数存在的问题。如果递归调用次数太多或调用层次太深,因函数调用发生的时空代价可能无法容忍。
c.递归思想和递归函数的应用场合。能够找到递推关系式子,可以将规模较大的原始问题划分成若干个规模较小的相似子问题(如分治法、动态规划算法等),具有递归结构的问题(如树形结构深度优先搜索等)。
(2)递归函数的设计。与普通函数的设计相比,递归函数的设计要注意以下问题。
a.需要将什么信息传递给下一层递归调用,由此确定递归函数有几个参数、各参数的含义是什么。
注意,在C/C+语言里,这些信息有时可以以全局变量的形式提供,此时可能就不需要相应
的参数了。
b.每一层递归函数调用后会得到一个怎样的结果,这个结果是否需要返回到上一层,由此确定递归函数的返回值及返回值的含义。
例如,本章案例2的递归函数g,需要将求得的q(n,m)返回到上一层。
c.在每一层递归函数的执行过程中,在什么情形下需要递归调用下一层。这一点应视不同问题而定。
e.递归前该做什么准备工作,递归返回后该做什么恢复工作。很多用深度优先搜索算法求解的题目都涉及这两个问题
f.递归函数执行到什么程度就不再需要递归调用下去了。
递归函数应该在适当的时候终止继续递归调用,也就是要确定递归的终止条件。如果递归函数的调用不能终止,会造成栈内存溢出,从而导致程序出错并终止运行。
(3)递归函数的调用。解题时应明确在main函数(或其他函数)中采取怎样的形式调用递归函数,也就是从怎样的初始状态出发进行递归调用,通常也就是确定实参的值。