1.02k likes | 1.14k Views
第三章 函数. 时间安排:4学时 3.1节2学时、其余6节2学时 教学目标:掌握函数的定义与调用方法 教学重点:函数定义、声明及调用方法 教学难点:递推、递归、复合函数设计要领. C++ 语言程序设计. 本章主要内容. 函数的定义和调用 函数间的参数传递 内联函数 带默认形参值的函数 函数重载 C++ 系统函数 深度探索. 2. 函数的定义. 函数是面向对象程序设计中,对功能的抽象 函数定义的语法形式 类型标识符 函数名(形式参数表) { 语句序列 }. 函数的声明与使用.
E N D
第三章 函数 时间安排:4学时 3.1节2学时、其余6节2学时 教学目标:掌握函数的定义与调用方法 教学重点:函数定义、声明及调用方法 教学难点:递推、递归、复合函数设计要领 C++语言程序设计
本章主要内容 函数的定义和调用 函数间的参数传递 内联函数 带默认形参值的函数 函数重载 C++系统函数 深度探索 2
函数的定义 函数是面向对象程序设计中,对功能的抽象 函数定义的语法形式 类型标识符 函数名(形式参数表) { 语句序列 } 函数的声明与使用 是被初始化的内部变量,寿命和可见性仅限于函数内部 若无返回值,写void 3
函数的定义 形式参数表 <type1> name1, <type2> name2, ..., <typen> namen 函数的返回值 由 return 语句给出,例如:return 0; 无返回值的函数(void类型),不必写return语句。 函数的声明与使用 4
函数的调用 调用前先声明函数: 若函数定义在调用点之前,则无需另外声明; 若函数定义在调用点之后,则需要在调用函数前按如下形式声明函数原型: 类型标识符 被调用函数名(含类型说明的形参表); 调用形式 函数名(实参列表) 嵌套调用 函数可以嵌套调用,但不允许嵌套定义。 递归调用 函数直接或间接调用自身。 函数的声明与使用 5
例3-1编写一个求x的n次方的函数 #include <iostream> using namespace std; //计算x的n次方 doublepower(double x, int n) { double val =1.0; while(n--)val *= x; return val; } int main() { cout << "5 to the power 2 is " << power(5,2) << endl; system("pause");return 0; } 函数的声明与使用 6
运行结果: 5to the power 2 is 25 例3-1编写一个求x的n次方的函数 函数的声明与使用 7
例3-2 数制转换 题目: 输入一个8位二进制数,将其转换为十进制数输出。 例如:11012=1(23)+1(22)+0(21)+1(20)=1310 所以,如果输入1101,则应输出13 函数的声明与使用 8
#include <iostream> using namespace std; //计算x的n次方 double power (double x, int n); int main() { int value = 0; cout << "Enter an 8 bit binary number "; for (int i = 7; i >= 0; i--) { char ch; cin >> ch; if (ch == '1') value += static_cast<int>(power(2, i)); } cout << "Decimal value is " << value << endl; return 0; } double power (double x, int n) { double val = 1.0; while(n--)val *= x; return val; } 运行结果: Enter an 8 bit binary number 01101001 Decimal value is 105 9
例3-3编写程序求π的值 其中arctan用如下形式的级数计算: 直到级数某项绝对值不大于10-15为止;π和x均为double型。 函数的声明与使用 10
#include <iostream> using namespace std; double arctan(double x) { double sqr = x * x; double e = x; double r = 0; int i = 1; while (e / i > 1e-15) { double f = e / i; r = (i % 4 == 1) ? r + f : r - f; e = e * sqr; i += 2; } return r; } 11
int main() { double a = 16.0 * arctan(1 / 5.0); double b = 4.0 * arctan(1 / 239.0); //注意:因为整数相除结果取整,如果参数写1/5,1/239,结果就都是0 cout << "PI = " << a - b << endl; return 0; } 运行结果: PI=3.14159 12
例3-4 寻找并输出11~999之间的数m,它满足m、m2和m3均为回文数。 回文:各位数字左右对称的整数。 例如:11满足上述条件112=121,113=1331。 分析: 10取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相等,则原数为回文。 函数的声明与使用 13
#include <iostream> using namespace std; //判断n是否为回文数 bool symm(unsigned n) { unsigned i = n; unsigned m = 0; while (i > 0) { m = m * 10 + i % 10; i /= 10; } return m == n; } 14
int main() { for(unsigned m = 11; m < 1000; m++) if (symm(m) && symm(m * m) && symm(m * m * m)) { cout << "m = " << m; cout << " m * m = " << m * m; cout << " m * m * m = " << m * m * m << endl; } return 0; } 15
运行结果: m=11 m*m=121 m*m*m=1331 m=101 m*m=10201 m*m*m=1030301 m=111 m*m=12321 m*m*m=1367631 16
例3-5 计算如下公式,并输出结果: 其中r、s的值由键盘输入。sin x的近似值按如下公式计算,计算精度为10-6: 函数的声明与使用 17
#include <iostream> #include <cmath> //对C++标准库中数学函数的说明 using namespace std; const double TINY_VALUE = 1e-10; double tsin(double x) { double g = 0; double t = x; int n = 1; do { g += t; n++; t = -t * x * x / (2 * n - 1) / (2 * n - 2); } while (fabs(t) >= TINY_VALUE); return g; } 18
int main() { double k, r, s; cout << "r = "; cin >> r; cout << "s = "; cin >> s; if (r * r <= s * s) k = sqrt(tsin(r) * tsin(r) + tsin(s) * tsin(s)); else k = tsin(r * s) / 2; cout << k << endl; return 0; } 运行结果: r=5 s=8 1.37781 19
例3-6投骰子的随机游戏 每个骰子有六面,点数分别为1、2、3、4、5、6。游戏者在程序开始时输入一个无符号整数,作为产生随机数的种子。 每轮投两次骰子,第一轮如果和数为7或11则为胜,游戏结束;和数为2、3或12则为负,游戏结束;和数为其它值则将此值作为自己的点数,继续第二轮、第三轮...直到某轮的和数等于点数则取胜,若在此前出现和数为7则为负。 由rolldice函数负责模拟投骰子、计算和数并输出和数。 函数的声明与使用 20
rand 函数原型:int rand(void); 所需头文件:<cstdlib> 功能和返回值:求出并返回一个伪随机数 srand 函数原型:void srand(unsigned int seed); 参数:seed产生随机数的种子。 所需头文件:<cstdlib> 功能:为使rand()产生一序列伪随机整数而设置起始点。使用1作为seed参数,可以重新初化rand()。 21
#include <iostream> #include <cstdlib> using namespace std; //投骰子、计算和数、输出和数 int rollDice() { int die1 = 1 + rand() % 6; int die2 = 1 + rand() % 6; int sum = die1 + die2; cout << "player rolled " << die1 << " + " << die2 << " = " << sum << endl; return sum; } 22
enum GameStatus { WIN, LOSE, PLAYING }; int main() { int sum, myPoint; GameStatus status; unsigned seed; cout << "Please enter an unsigned integer: "; cin >> seed;//输入随机数种子 srand(seed);//将种子传递给rand() sum = rollDice(); //第一轮投骰子、计算和数 23
switch (sum) { case 7: //如果和数为7或11则为胜,状态为WIN case 11: status = WIN; break; case 2: //和数为2、3或12则为负,状态为LOSE case 3: case 12: status = LOSE; break; default: //其它情况,游戏尚无结果,状态为PLAYING,记下点数,为下一轮做准备 status = PLAYING; myPoint = sum; cout << "point is " << myPoint << endl; break; } 24
while (status == PLAYING) { //只要状态仍为PLAYING,就继续进行下一轮 sum = rollDice(); if (sum == myPoint) //某轮的和数等于点数则取胜 status = WIN; else if (sum == 7) //出现和数为7则为负 status = LOSE; } //当状态不为PLAYING时上面的循环结束,以下程序段输出游戏结果 if (status == WIN) cout << "player wins" << endl; else cout << "player loses" << endl; return 0; } 25
运行结果2: Please enter an unsigned integer:23 player rolled 6 + 3 = 9 point is 9 player rolled 5 + 4 = 9 player wins 26
嵌套调用 函数的声明与使用 main{} 调fun1() 结束 fun1() 调fun2() 返回 fun2() 返回 ② ③ ④ ① ⑤ ⑨ ⑦ ⑧ ⑥ 27
例3-6 输入两个整数,求平方和。 #include <iostream> using namespace std; int fun2(int m) { return m * m; } int fun1(int x,int y) { return fun2(x) + fun2(y); } 函数的声明与使用 28
int main() { int a, b; cout << "Please enter two integers (a and b): "; cin >> a >> b; cout << "The sum of square of a andb: " << fun1(a, b) << endl; return 0; } 运行结果: Please enter two integers(a and b): 3 4 The sum of square of a and b: 25 29
小结与作业 • C++中的函数与数学所讲的函数有联系,但有区别: • 相同点:命名、自变量、函数值(返回值)、复合函数 • 区别:函数值可以是void(空),一个函数完成一项任 • 务,主要用于模块化设计 • C++函数按调用区分为:被调函数(下级)、主调函数(上级) • 函数之间的信息传递主要靠函数值、参数,前者下级向上级传,后者上级向下级传(特殊情况,可以回传) • 应坚持先定义后使用的原则,若要使用后面定义的函数,可以使用声明的方法。 作业:P96:3-1~3-10
第三章 函数 时间安排:4学时 3.1节2学时、其余6节2学时 教学目标:掌握函数的定义与调用方法 教学重点:函数定义、声明及调用方法 教学难点:递推、递归、复合函数设计要领 C++语言程序设计
本节主要内容 递推函数 递归函数 函数间的参数传递 内联函数 带默认形参值的函数 函数重载 C++系统函数 32
递推算法是由简单问题开始,使用归纳推理的思想,逐步递推,最终得到问题的解。 使用递推算法解决问题,首先要分析问题,从中得到一个递推公式,递推公式是用已知项描述未知项的公式,它有一个适用范围,有一个递推初值。如用sn表示从1到n的累加和,则显然有: s1=1 s2=s1+2 s3=s2+3 ......... si=si-1+i 最后的si=si-1+i 称为递推公式,适用范围为i=2、3、...、n s1=1称为递推初值。 递推函数(补充内容)
对前述的递推增加一个递推初值 s0=0,则s1=1变为 s1=s0+1 s2=s1+2 s3=s2+3 ......... si=si-1+i 最后的递推公式si=si-1+i 的适用范围为i=1、2、3、...、n 使用递推公式,注意公式的适用范围,同时注意递推初始值,可以十分方便的编写出递推程序。 递推函数(补充内容)
对前述的求从1到n求和的问题编写函数如下: int sum(int n) { int s=0; for(int i=1;i<=n;i++) s=s+i; return s; } 递推函数(补充内容)
递推函数(补充内容) • 模仿从求从1到n的和的递推函数的编写方法,挑学生写出以下问题的递推公式,并注明初值、适用范围: • 1、求p=n! • 2、求pow=mn • 3、求斐波那契数列
递归是函数直接或间接调用自身的一种函数调用方式。 递归算法是由复杂问题开始,将其转化为一个与其相似的但规模小的问题的组合,逐步进行,最终转化为可直接求解的具体问题。 使用递归算法解决问题,首先要分析问题,从中得到一个递归公式,递归公式是用较小规模的问题描述现有规模的问题,它有一个描述问题规模的参数,每次递归调用,这个参数都要变化,即问题的规模逐步简化,最终成为一个不再递归而得到答案的简单问题,这个简单问题就是递归出口。 如求n!,显然 当n=0时,可规定:n!=1 ,这就是递归出口 当n>0时,有n!=(n-1)!*n ,这就是递归公式(递归调用) 递归函数及其实现
递归函数及其实现 用递归算法求 n! 定义:函数 fact( n ) = n! fact( n-1 ) = ( n-1 )! 则有 fact( n ) = n fact( n-1 ) 已知 fact( 1 ) = 1
为了表述得直观清晰,我们定义两个结点:或结点和与结点。为了表述得直观清晰,我们定义两个结点:或结点和与结点。 图示的直观性与思维助力。 1、或结点 A为“或结点”,A依不同条件会有两种不同的取值, B 或C。结点用 表示。
如果有多于 2 种取值,可用下图: 条件为 Z1 , Z2 ,… ,Zn , A 取值为 B 或 C,…或 G
2、与结点 与结点要涂黑,相关联的 B 与 C 之间要用弧线连起来。 A 为与结点,A 的最终取值为 C 结点的值,但为了求得 C 的值,得先求出 B 结点的值, C 是B 的函数。
仍以求 n ! 为例画出如下与或图 A 为或结点;B 为直接可解结点,值为1; C 为与结点,当 n>1 时,A 的取值即 C 的值,而 C 的值即 E 的值,为了求得 E 的值,需要先求出 D 的值。D 值 fact( n-1 ) 乘以 n 即为 E 的值。
与结点可能有多个相关联的点,这时可描述为下图与结点可能有多个相关联的点,这时可描述为下图 A 结点的值最终为 D 的值,但为了求 D 需先求 B 和 C。从图上看, 先求左边的点才能求最右边的点的值,我们约定最右边 D 点的值就是 A 结点的值。
下面我们以 3!为例来画与或结点图,目的是体会 递归的含义。 C = 1 D = 2*C = 2 B = D = 2 E = 3*B = 3*2 = 6 A = E = 6
从图可以想象: 欲求 fact( 3 ),先要求 fact( 2 );要求 fact( 2 ) 先求 fact( 1 )。就象剥一颗圆白菜,从外向里,一层层剥下来,到了菜心,遇到 1 的阶乘,其值为1,到达了递归的边界。然后再用 fact( n )=n*fact( n-1 ) 这个普遍公式,从里向外倒推回去得到 fact( n ) 的值。 为了把这个问题说得再透彻一点。我们画了如下的流程图:
在这个图中“内层”与“外层”有着相同的结构。它们之间“你中有我,我中有你”,呈现相互依存的关系。在这个图中“内层”与“外层”有着相同的结构。它们之间“你中有我,我中有你”,呈现相互依存的关系。 为了进一步讲清递归的概念,将递归与递推做一比较。仍以求阶乘为例。 递推是从已知的初始条件出发,逐次去求所需要的阶乘值。 如求 3! 初始条件 fact( 1 ) = 1 fact( 2 ) =2 * fact( 1 ) = 2 fact( 3 ) = 3 * fact( 2 ) = 6
这相当于从菜心“推到”外层。 递归算法的出发点不放在初始条件上,放在求解的目标上,从所求的未知项出发逐次调用本身的求解过程,直到递归的边界(即初始条件)。就本例而言,读者会认为递归算法可能是多余的,费力而不讨好。但许多实际问题不可能或不容易找到显而易见的递推关系,这时递归算法就表现出了明显的优越性。下面我们将会看到,递归算法比较符合人的思维方式,逻辑性强,可将问题描述得简单扼要,具有良好的可读性,易于理解. 许多看来相当复杂,或难以下手的问题,如果能够使用递归算法就会使问题变得易于处理。下面举一个尽人皆知的例子河内(Hanoi)塔问题。