高等学校计算机基础教育教材 Visual Basic 6.0 程序设计 上海中医药大学现代教育技术中心 计算机教学部
字符串处理 例 编写一个英文打字训练的程序.要求如下: (1)在标签框内随机产生30个字母的范文; (2)当焦点进入文本框时开始计时,并显示当时时间; (3)在键入文本框按产生的范文输入相应的字母; (4)当键入满了30个字母后结束计时,禁止向文本框输入 内容 ,与范文逐一比较,显示打字的速度和正确率。
第六章 过 程 1 函数过程的定义与调用 2 子过程的定义与调用 3 参数传递 4 递归 5 常用算法(三)
用子(函数)过程解决问题 子(函数)过程把较大的任务分解成若干个较小的任务,并提炼出公用任务 程序员可以在其他函数的基础上构造程序,而不需要从头做起 设计得当的子(函数)过程可以把具体操作细节对程序中不需要知道它们的那些部分隐藏掉,从而使整个程序结构清楚 分而治之、信息隐藏
6.1 函数过程的定义 1. 引例6.1 除了系统提供的内部函数过程和事件过程外,用户可自定义过程: 以Sub保留字开始的为子过程; 以Function保留字开始的为函数过程。 1. 引例6.1 已知多边形的各条边的长度,要计算多边形的面积。 计算多边形面积,可将多边形分解成若干个三角形。 计算三角形面积的公式如下: x y z 定义函数过程area: Public Function area(x!, y!, z!) As Single Dim c! c = 1 / 2 * (x + y + z) area = Sqr(c * (c - x) * (c - y) * (c - z)) End Function 调用函数过程: Sub command1_click() …… 输入若干个三角形边长 S=area(a,b,c)+area(c,d,e) S=S+area(e,f,g) Print S End Sub
2.函数过程的定义 自定义函数过程有两种方法: (1)利用“工具”菜单下的“添加过程”命令定义,生成一个函数的框架。 (2)利用代码窗口直接定义。 函数过程形式: Function 函数过程名([参数列表]) [As 类型] 局部变量或常数定义 语句块 函数名 = 返回值 函数过程体 [Exit Function] 函数名 = 返回值 End Function 函数过程名:命名规则同变量名 参数列表形式: [ByVal]变量名[()][As 类型] 称为形参或哑元,仅表示参数的个数、类型,无值。 函数名 = 返回值 在函数体内至少对函数名赋值一次。 [Exit Function] : 表示退出函数过程。
Function MyReplace(s$, OldS$, NewS$) As String 例6.2 定义MyReplace(S,OldS,NewS)函数过程,具有Replace函数功能。 Function MyReplace(s$, OldS$, NewS$) As String Dim i%, lenOldS% lenOldS = Len(OldS) ' 取OldS字符子串长度 i = InStr(s, OldS) ' 在字符串中找有否OldS字符子串 Do While i > 0 ' 找到用NewS 字符子串替换OldS字符子串 s = Left(s, i - 1) + NewS + Mid(s, i + lenOldS) i = InStr(s, OldS) ' 找下一个OldS字符子串 Loop MyReplace = s ' 替换后的字符串赋值给函数过程名 End Function 假定S为“abcdefgabcd”,Oolds为“cd”,News为“3”,调用MyReplace函数的返回值为“ab3efgab3”。上述程序循环执行2次: (“abcdefgabcd”,“cd”,“3”) 第1 次 i=3 结果 “ab3efgabcd” (“ab3efgabcd”,“cd”,“3”) 第2 次 i=9 结果 “ab3efgab3”
参数列表:称为实参或实元,它必须与形参个数相同,位置与类型一一对应。可以是同类型的常量、变量、表达式。 3.函数过程的调用 函数过程调用同标准函数调用,形式:函数过程名([参数列表]) 参数列表:称为实参或实元,它必须与形参个数相同,位置与类型一一对应。可以是同类型的常量、变量、表达式。 :
6.2 子 过 程 函数过程的不足: (1)不是为了获得某个函数值,而是为了某种功能的处理,如例1.1。 (2) 要获得多个结果。 6.2 子 过 程 函数过程的不足: (1)不是为了获得某个函数值,而是为了某种功能的处理,如例1.1。 (2) 要获得多个结果。 1.引例6.3 编写一个两个数交换的过程供多次调用。 Swap (x,y)子过程的定义 主调程序调用Swap子过程 Public Sub Swap(x, y) Private Sub Form_Click() Dim t Dim a, b t = x a = 10 x = y b = 20 y = t Call Swap (a, b) End Sub Print "a=";a,",b="; b End Sub
(1)函数过程名有值,有类型,在函数体内至少赋值一次; 子过程名无值,无类型,在子过程体内不能对子过程名赋值; 2.子过程定义 Sub 子过程名[(参数列表)] 局部变量或常数定义 语句 [Exit Sub] End Sub 3.子过程的调用 子过程名 [参数列表] 或 Call 子过程名(参数列表) 4.子过程与函数过程区别: (1)函数过程名有值,有类型,在函数体内至少赋值一次; 子过程名无值,无类型,在子过程体内不能对子过程名赋值; (2)调用时,子过程调用是一句独立的语句。 函数过程不能作为单独的语句加以调用,必须参与表达式运算。 (3)一般当过程有一个函数值,使用函数过程较直观; 反之若过程无返回值,或有多个返回值,使用子过程较直观。
6.3 参数传递 Sub Swap2(x%, y%) t% = x: x = y: y = t End Sub 指主调过程的实参传递给被调过程的形参。 1. 传址与传值 传址: 形参得到的是实参的地址,当形参值的改变同时也改变实参的值。 传值: 形参得到的是实参的值,形参值的改变不会影响实参的值。 例6.5 两个变量的交换。 Sub Swap1(ByVal x%, ByVal y%) t% = x: x = y: y = t End Sub Private Sub Command1_Click() a% = 10: b% = 20: Swap1 a, b '传值 Print "A1="; a, "B1="; b a = 10: b = 20: Swap2 a, b '传址 Print "A2="; a, "B2="; b 例6.6 求若干个数的最大公约数。 Sub Swap2(x%, y%) t% = x: x = y: y = t End Sub
当参数是数组通过传址方式进行传递。注意: 2. 数组参数的传递 当参数是数组通过传址方式进行传递。注意: 在实参和形参中写数组名,忽略维数的定义,但圆括号不能省。 被调过程可通过Lbound和Ubound函数确定实参数组的下、上界。 Lbound和Ubound函数的形式如下: {L|U}bound(数组名[,维数]) 其中:维数指明要测试的是第几维的下标值,缺省是一维数组。 例 6.7 编一函数tim,求任意一维数组中各元素之积。 调用tim,求 和 Function tim(a() As Integer) Dim t#, i% t = 1 For i = Lbound(a) To Ubound(a) t = t * a(i) Next i tim = t End Function 调用: Sub Command1_Click() Dim a%(1 To 5),b%(3 To 8) … t1# = tim(a()) t2 #= tim(b()) Print t1, t2 End Sub
子过程(函数)的嵌套调用 在被调子过程(函数)中,又调用了其他子过程(函数)----嵌套调用 标准事件过程 { … ... } a 子过程 B子过程 { … } ③ ① ② ④ ⑥ ⑦ ⑤
使用过程注意事项: 1. 确定自定义的过程是子过程还是函数过程 函数过程名有值,子过程名无值。 2.过程中形参的个数和传递方式的确定 过程中参数的作用是实现过程与调用者的数据通信。 (1)从主调程序获得初值,值传递。 (2)将结果返回给主调程序,地址传递。 3. 实参与形参结合时对应问题 个数、类型、位置、次序一一对应。 形参是值传递,对应实参可以是表达式、常量、数组元素。 形参是地址传递,对应实参只能是简单变量。 数组、记录类型、对象只能是地址传递。
实验6.2子过程DeleStr(s1,ByVal s2 As String)形参的确定 实验6.3函数过程 MaxLength(s)形参的确定 s 要处理的字符串,值传递。 MaxLength函数名,最长的单词长度。 实验6.4回文数的判断中形参的确定 1. 函数过程,用一个形参,值传递对所判断的数字;函数名是否为回文数。 Function IsH(ByVal ss As String) As Boolean 2. 子过程,用两个形参,值传递对所判断的数字,地址传递是否为回文数。 Sub hui(ByVal ss As String, Tag As Boolean)
递归过程 过程在自身定义的内部调用自己。 例6.10 编fac(n)=n! 的递归函数 Function fac(n As Integer) As Integer If n = 1 Then fac = 1 Else fac = n * fac(n - 1) End If End Function Sub Command1_Click() Print “fac(4)=”;fac(4) End Sub 结果: fac(4)=24
递推 回归 在递归处理中,用栈来实现。栈中存放形参、局部变量、返回地址。 递推过程:每调用自身,当前参数压栈,直到达到递归结束条件。 fac(4)=4*fac(3) fac(3)=3*fac(2) fac(2)=2*fac(1) fac(1)=1 fac(4)=4*6 fac(3)=3*2 fac(2)=2*1 在递归处理中,用栈来实现。栈中存放形参、局部变量、返回地址。 递推过程:每调用自身,当前参数压栈,直到达到递归结束条件。 回归过程:不断从栈中弹出当前的参数,直到栈空。 递归算法设计简单,但消耗的机时和占据的内存空间比非递归大。 思考: 若上述fac函数中少了 : If n = 1 Then fac = 1 即仅有语句: fac = n * fac(n - 1) 或 n<=0 程序运行将造成何结果?由此可见构成递归的结构如下: 递归结束条件及结束时的值; 能用递归形式表示,并且递归向终止条件发展。
分析以下子过程的功能 ,当n=100,r=8,结果是多少? Public Sub f(ByVal n %, ByVal r %) If n >r Then Call f(n \ r, r) ' ① Print n Mod r; ' ② End Sub Private Sub Command1_Click() Call f(100, 8) 显示结果 1 4 4
6.6 常用算法(三) 1.数制转换 例1 将一个十进制整数m转换成 r (2~16)进制字符串。
例2加密和解密 简单加密的思想是: 将每个字母C加一序数K,式子 c=chr(Asc(c)+k), 例如序数k为5,这时 “A”“F”,“a”“f”,“B”“G”… 当加序数后的字母超过“Z”或“z”则= chr(Asc(c)+k -26)。 解密为加密的逆过程。 返回224
查找 (1)顺序查找 例3。 顺序查找根据查找的关键值与数组中的元素逐一比较(数组可无序) Public Sub Search(a() As Variant, ByVal key As Variant, index%) Dim i% For i = LBound(a) To UBound(a) If key = a(i) Then ' 找到,元素的下标在index中,结束查找 index = i Exit Sub End If Next i index = -1 ' 找不到, index形参的值为-1 End Sub 平均查找次数 n/2
本例用递归实现例4 Sub birsearch(a(), low%, high%, key , index%) (2)二分法查找 要查找的数组必须有序。 思想:要查找的关键值Key同数组的中间mid项元素比较: Key<a(mid) high=mid-1 查找区域缩小一半,继续 Key=a(mid) 找到 结束 Key>a(mid) low=mid+1 查找区域缩小一半,继续 12 34 56 78 111 222 333 444 555 666 777 888 Low high mid 直到找到或查找区域中无元素. 本例用递归实现例4 Sub birsearch(a(), low%, high%, key , index%) 444 Key
有矩形法、梯形法、抛物线法(又称辛卜生法)等。 梯形法积分的思想是: 将积分区间[a,b]n等分,小区间的长度为, 例5数值积分 有矩形法、梯形法、抛物线法(又称辛卜生法)等。 梯形法积分的思想是: 将积分区间[a,b]n等分,小区间的长度为, 第i块小矩形的近似面积为: 整个积分的结果为这n块小面积的累加,即 : Public Function trapez(ByVal a!, ByVal b!, ByVal n%) As Single Dim sum!, h!, x! h = (b - a) / n sum = (f(a) + f(b)) / 2 For i = 1 To n - 1 x = a + i * h sum = sum + f(x) Next i trapez = sum * h End Function
课后习题: 125页,第1、2、3、4题