1、第八章第八章 子程序和函数子程序和函数 8.1 子程序概述子程序概述 在在VB5.0/6.0中,除了系统提供的内部函数过中,除了系统提供的内部函数过程和事件过程外,用户可自定义过程下列四种过程和事件过程外,用户可自定义过程下列四种过程:程:“Sub”保留字开始的为子过程保留字开始的为子过程“Function”保留字开始的为函数过程保留字开始的为函数过程“Property”保留字开始的为属性过程保留字开始的为属性过程“Event”保留字开始保留字开始的事件过程的事件过程 在在VB中,的中,的Sub子过程分为:事件过程和通用过程子过程分为:事件过程和通用过程 8.2.1 事件过程事件过程 (1)窗
2、体事件的语法)窗体事件的语法Private Sub Form_事件名事件名(参数列表参数列表)语句组语句组End Sub(2)控件事件的语法)控件事件的语法Private Sub 控件名控件名_事件名事件名(参数列表参数列表)语句组语句组 End Sub8.2 Sub过程过程子过程的定义形式如下:子过程的定义形式如下:Public|PrivateStatic Sub 子过程名子过程名(形参表形参表)语句组语句组 Exit Sub 语句组语句组End Sub 8.2.2 通用过程(通用过程(Sub过程)的定义过程)的定义 说明:说明:(1)子过程名:命名规则与变量名规则相同。子过程名不)子过程名
3、:命名规则与变量名规则相同。子过程名不返回值,而是通过形参与实参的传递得到结果,调用时可返回值,而是通过形参与实参的传递得到结果,调用时可返回多个值。返回多个值。(2)形式参数列表:形式参数通常简称)形式参数列表:形式参数通常简称“形参形参”,仅表示,仅表示形参的类型、个数、位置,定义时是无值的,只有在过程被形参的类型、个数、位置,定义时是无值的,只有在过程被调用时,虚实参结合后才获得相应的值。调用时,虚实参结合后才获得相应的值。(3)过程可以无形式参数,但括号不能省。)过程可以无形式参数,但括号不能省。(4)参数的定义形式:)参数的定义形式:ByValByRef变量名变量名()As 类型类型
4、,,ByVal表示当该过程被调用时,参数是按值传递的;缺省或表示当该过程被调用时,参数是按值传递的;缺省或ByRef表示当该过程被调用时,参数是按地址传递的。表示当该过程被调用时,参数是按地址传递的。例例8.1编一个交换两个整型变量值的子过程。编一个交换两个整型变量值的子过程。Private Sub Swap(X As Integer,Y As Integer)Dim temp As Integer Temp=X:X=Y:Y=TempEnd Sub8.2.3 子过程的建立子过程的建立 通过通过“工具工具”菜单中的菜单中的“添加过程添加过程”命令,出现命令,出现“添加过添加过程程”对话框(见图对
5、话框(见图6.1),选择过程类型(子过程、函数、属),选择过程类型(子过程、函数、属性、事件)及作用范围(公有的性、事件)及作用范围(公有的Public、私有的、私有的Private),),单击确定后得到一个过程或函数定义的结构框架(模板),单击确定后得到一个过程或函数定义的结构框架(模板),如:如:Public Sub Sort()End Sub8.2.4 过程的调用过程的调用 1 子过程的调用子过程的调用 子过程名子过程名 参数列表参数列表 或或 Call 子过程名子过程名(参数列表参数列表)2 函数过程的调用函数过程的调用 变量名变量名=函数过程名函数过程名(参数列表参数列表)说明:说明
6、:(1)参数列表参数列表称为实参或实元,它必须与形参保持个数相称为实参或实元,它必须与形参保持个数相同,位置与类型一一对应。同,位置与类型一一对应。(2)调用时把实参值传递给对应的形参。其中值传递(形调用时把实参值传递给对应的形参。其中值传递(形参前有参前有ByVal说明)时实参的值不随形参的值变化而改变。而说明)时实参的值不随形参的值变化而改变。而地址传递时实参的值随形参值的改变而改变。地址传递时实参的值随形参值的改变而改变。(3)当参数是数组时,形参与实参在参数声明时应省略当参数是数组时,形参与实参在参数声明时应省略其维数,但括号不能省。其维数,但括号不能省。(4)调用子过程的形式有两种,
7、用调用子过程的形式有两种,用Call关键字时,实参关键字时,实参必须加圆括号括起,反之则实参之间用必须加圆括号括起,反之则实参之间用“,”分隔。分隔。例:调用上面定义的例:调用上面定义的Sawp子过程的形式:子过程的形式:Swap a,b Call Swap(a,b)Private Sub CmdOK()Call Swap(a,b)Swap a,bEnd SubPrivate Sub Swap(x%,y%)Dim temp%Temp=X X=Y Y=TempEnd Sub 过程调用的执行过程:过程调用的执行过程:8.3 Function过程过程 Visual Basic函数分为内部函数和外部函
8、数,外部函数函数分为内部函数和外部函数,外部函数是用户根据需要用是用户根据需要用Function关键字定义的函数过程,关键字定义的函数过程,与子与子过程不同的是函数过程将返回一个值过程不同的是函数过程将返回一个值。8.3.1 函数过程(函数过程(Function过程)的定义过程)的定义 Public|PrivateStaticFunction 函数名函数名()As函数体函数体函数名函数名=返回值返回值Exit Function函数名函数名=返回值返回值End Function函数体函数体说明:说明:(1)函数名:命名规则与变量名规则相同。但不能与系统的)函数名:命名规则与变量名规则相同。但不能
9、与系统的内部函数或其它通用子过程同名,也不能与已定义的全局变内部函数或其它通用子过程同名,也不能与已定义的全局变量和本模块中模块级变量同名。量和本模块中模块级变量同名。(2)在函数体内,函数名可以当变量使用,函数的返回值就)在函数体内,函数名可以当变量使用,函数的返回值就是通过对函数名的赋值语句来实现的,在函数过程中至少要是通过对函数名的赋值语句来实现的,在函数过程中至少要对函数名赋值一次。对函数名赋值一次。(3)AS类型:是指函数返回值的类型,若省略,则函数返类型:是指函数返回值的类型,若省略,则函数返回变体类型值(回变体类型值(Variant)。)。(4)Exit Function:表示退
10、出函数过程,常常是与选择结构:表示退出函数过程,常常是与选择结构(If或或Select Case语句)联用,即当满足一定条件时,退出函语句)联用,即当满足一定条件时,退出函数过程。数过程。(5)形参数列表:形参的定义与子过程完全相同。)形参数列表:形参的定义与子过程完全相同。例例6.2 8.3.2 函数的调用函数的调用调用形式如下:调用形式如下:函数名(实参列表)函数名(实参列表)说明:说明:在调用时实参和形参的数据类型、顺序、个数必须匹在调用时实参和形参的数据类型、顺序、个数必须匹配。函数调用只能出现在表达式中,其功能是求得函数配。函数调用只能出现在表达式中,其功能是求得函数的返回值。的返回
11、值。8.4 过程的作用域过程的作用域Public|Private Sub 子过程名子过程名(形式参数列表形式参数列表).End Sub概念概念 通用子过程和函数过程既可写在通用子过程和函数过程既可写在窗体模块中窗体模块中也可写在也可写在标准模块中标准模块中,在定义时可选用关键字,在定义时可选用关键字PrivatePrivate(局部)(局部)和和PublicPublic(全局)(全局),来决定它们能被调用的范围。,来决定它们能被调用的范围。1、窗体窗体/模块级过程:模块级过程:加加Private关键字的过程,只能被定义的窗体或模关键字的过程,只能被定义的窗体或模 块中块中的过程调用。的过程调用
12、。2、全局级过程:全局级过程:加加Public关键字关键字(或缺省)(或缺省)的过程,可供该应用程序的的过程,可供该应用程序的所有窗体和所有标准模块中的过程调用。所有窗体和所有标准模块中的过程调用。按过程的作用范围来划分,过程可分为:按过程的作用范围来划分,过程可分为:模块级过程模块级过程 全局级过程全局级过程用例7.1来说明过程的作用域 总结归纳过程的定义及作用域如下表(教材总结归纳过程的定义及作用域如下表(教材Pg.163)8.5 过程之间参数的传递过程之间参数的传递Visual Basic中不同模块(过程)之间数据的传递有两种中不同模块(过程)之间数据的传递有两种方式:方式:通过过程调用
13、实参与形参的结合实现;通过过程调用实参与形参的结合实现;使用全局变量来实现各过程中共享数据。使用全局变量来实现各过程中共享数据。8.5.1 8.5.1 形式参数与实际参数形式参数与实际参数1、形式参数、形式参数 是指在定义通用过程时,出现在是指在定义通用过程时,出现在Sub或或Function语句中语句中的变量名后面园括号内的数,是用来接收传送给子过程的的变量名后面园括号内的数,是用来接收传送给子过程的数据,形参表中的各个变量之间用逗号分隔。数据,形参表中的各个变量之间用逗号分隔。2、实际参数、实际参数 实际参数是指在调用实际参数是指在调用Sub或或Function过程时,写入子过程过程时,写
14、入子过程名或函数名后括号内的参数,其作用是将它们的数据(数值名或函数名后括号内的参数,其作用是将它们的数据(数值或地址)传送给或地址)传送给Sub或或Function过程与其对应的形参变量。过程与其对应的形参变量。实参可由常量、表达式、有效的变量名、数组名(后加左、实参可由常量、表达式、有效的变量名、数组名(后加左、右括号,如右括号,如A())组成,实参表中各参数用逗号分隔。)组成,实参表中各参数用逗号分隔。8.5.2 参数传递(虚实结合)参数传递(虚实结合)参数传递指主调过程的实参(调用时已有确定值和内存参数传递指主调过程的实参(调用时已有确定值和内存地址的参数)传递给被调过程的形参,参数的
15、传递有两种方地址的参数)传递给被调过程的形参,参数的传递有两种方式:按值传递、按地址传递。形参前加式:按值传递、按地址传递。形参前加“ByVal”关键字的关键字的是按值传递,缺省或加是按值传递,缺省或加“ByRef”关键字的为按地址传递。关键字的为按地址传递。传址与传值传址与传值 传址:传址:形参得到的是实参的地址,当形参值的改变同形参得到的是实参的地址,当形参值的改变同时也改变实参的值。时也改变实参的值。传值:传值:形参得到的是实参的值,形参值的改变不会影形参得到的是实参的值,形参值的改变不会影响实参的值。响实参的值。Sub Swap2(x%,y%)Dim Temp%Temp=x:x=y:y
16、=Temp End SubSub Swap1(ByVal x%,ByVal y%)Dim Temp%Temp=x:x=y:y=TempEnd Sub例例8.4 Private Sub CmdOK_Click()a%=10:b%=20:Swap1 a,b 传传 值值 Print A1=;a,B1=;b a=10:b=20:Swap2 a,b 传地址传地址 Print A2=;a,B2=;bEnd Sub程序调用执行过程:值传递的执行过程如下图:值传递的执行过程如下图:通过地址传递数据的执行过程通过地址传递数据的执行过程 数据传递的几点说明:数据传递的几点说明:1.参数的数据类型参数的数据类型2.
17、按值传递参数按值传递参数形参是实参的一个副本形参是实参的一个副本3.按地址传递参数按地址传递参数形参实参公用内存单元形参实参公用内存单元4.子过程执行一系列操作,函数过程返回一个值子过程执行一系列操作,函数过程返回一个值注意:如果实参是常量(系统常量、符号常量)或者表达注意:如果实参是常量(系统常量、符号常量)或者表达式,则无论定义时使用值传递还是地址传递,都是按值传式,则无论定义时使用值传递还是地址传递,都是按值传递将常量或者表达式的计算值传递给形参。递将常量或者表达式的计算值传递给形参。8.6 高级用法高级用法(数组做为参数数组做为参数)Visual Basic允许把数组作为实参传送到过程
18、中。数组作允许把数组作为实参传送到过程中。数组作参数是通过传地址方式传送。在传送数组时,除遵守参数参数是通过传地址方式传送。在传送数组时,除遵守参数传送的一般规则外,还应注意以下几点:传送的一般规则外,还应注意以下几点:1把一个数组的全部元素传送给一个过程,应将数组名分把一个数组的全部元素传送给一个过程,应将数组名分别写入形参列表中,并略去数组的上下界,但括号不能省别写入形参列表中,并略去数组的上下界,但括号不能省略。略。Private Sub Sort(a()As single)End Sub其中形参其中形参“a()”即为数组。即为数组。2被调过程可通过被调过程可通过Lbound和和Ubou
19、nd函数确定实参数组的函数确定实参数组的上、下界。上、下界。3当用数组作形参时,对应的实参必须也是数组,且类型当用数组作形参时,对应的实参必须也是数组,且类型一致。一致。4实参和形参结合是按地址传递,即形参数组和实参数组实参和形参结合是按地址传递,即形参数组和实参数组共用一段内存单元。共用一段内存单元。例如:定义了实参数组例如:定义了实参数组b(1 to 8),给它们赋了值,调用,给它们赋了值,调用Sort()函数过程的形式如下:函数过程的形式如下:Sort b()或或 Call Sort(b()实参数组后面的括号可以省略,但为便于阅读,建议一般实参数组后面的括号可以省略,但为便于阅读,建议一
20、般不要省略为好。不要省略为好。调用时形参数组调用时形参数组a和实参数组和实参数组b虚实结合,共用一段内存单元,虚实结合,共用一段内存单元,如下所示。因此在如下所示。因此在Sort()过程中改变数组过程中改变数组a的各元素值,也就的各元素值,也就相当于改变了实参数组相当于改变了实参数组b中对应的元素的值,当调用结束时,中对应的元素的值,当调用结束时,形参数组形参数组a成为无定义。成为无定义。b(1)b(2)b(3)b(4)b(5)b(6)b(7)b(8)a(1)a(2)a(3)a(4)a(5)a(6)a(7)a(8)参数为数组时虚实结合示意图参数为数组时虚实结合示意图 例例6.7改写上章例改写上
21、章例5.3的排序程序,分别将排序用子过程的排序程序,分别将排序用子过程Sort(),产生,产生N个随机整数用子过程个随机整数用子过程GetData(),输入,输入N个数组个数组元素用元素用PrData()子过程来完成。子过程来完成。补充例题:编写判断一个整数是不是素数写函数过程。补充例题:编写判断一个整数是不是素数写函数过程。如果给定的整数是素数,则返回逻辑值如果给定的整数是素数,则返回逻辑值True,否则返回,否则返回False Private Function Prime(n As Integer)As Boolean Dim k%,Yes As Boolean Yes=True For
22、k=2 to n2 IF n mod k=0 then Yes=False:Exit For Next k Prime=Yes 给函数名赋值,作为函的返回值给函数名赋值,作为函的返回值End Function如何调用该函数呢?如何调用该函数呢?使用该函数,编一验证哥德巴赫猜想。使用该函数,编一验证哥德巴赫猜想。Private Sub Form_Click()Dim n%,n1%,n2%n=Val(InputBox(输入大于输入大于6的正整数的正整数)For n1=3 to n2 step 2 让让n1从从3开始分解开始分解 n2=n-n1 求得求得n2 如果如果n1和和n2都是素数,则打印输出
23、都是素数,则打印输出 If prime(n1)And prime(n2)then Print n&=&n1&+&n2 Exit For 结束循环结束循环 End if Next n1End SubFor i=1 To n-1For i=1 To n-1 p=i p=i For j=i+1 To n For j=i+1 To n If a(p)a(j)Then p=j If a(p)a(j)Then p=j Next j Next j temp=a(i)temp=a(i)a(i)=a(p)a(i)=a(p)a(p)=temp a(p)=tempNext iNext i选择法排序(升序)的选择法排
24、序(升序)的VB程序程序:如何写一个排序的如何写一个排序的VB过程?过程?补充例题补充例题2:排序子过程:排序子过程Private Sub Sort(a()As single)Private Sub Sort(a()As single)Dim i%,j%,p%,n%,temp!Dim i%,j%,p%,n%,temp!n=Ubound(a)n=Ubound(a)For i=1 To n-1 For i=1 To n-1 p=i p=i For j=i+1 To n For j=i+1 To n If a(p)a(j)Then p=j If a(p)a(j)Then p=j Next j Nex
25、t j temp=a(i)temp=a(i)a(i)=a(p)a(i)=a(p)a(p)=temp a(p)=temp Next I Next IEnd SubEnd Sub8.7 过程的嵌套和递归调用过程的嵌套和递归调用8.7.1 过程的嵌套过程的嵌套 在程序中调用一子过程,而在子过程中又调用另外的子过在程序中调用一子过程,而在子过程中又调用另外的子过程,这种程序结构称为程,这种程序结构称为过程的嵌套。过程的嵌套。过程的嵌套调用执行过过程的嵌套调用执行过程如程如下图:下图:8.7.2 过程的递归调用过程的递归调用 用自身的结构来描述自身,称递归。例如,对阶乘的定义:用自身的结构来描述自身,称
26、递归。例如,对阶乘的定义:)!2()1()!1()!1(!nnnnnn Visual Basic允许在一个允许在一个Sub子过程和子过程和Function过程的定过程的定义内部调用自己,即递归义内部调用自己,即递归Sub子过程和递归子过程和递归Function函数。函数。例例8.8编求阶乘编求阶乘fac(n)=n!的递归函数的递归函数Private Function fac(n As Integer)As Integer If n=1 Then fac=1 Else fac=n*fac(n-1)End IfEnd FunctionPrivate Sub Form_Click()Print“fa
27、c(4)=”;fac(4)End Sub程序的递归过程:程序的递归过程:说明:说明:递归处理一般用栈来实现,分递推和回归两个过程,如上递归处理一般用栈来实现,分递推和回归两个过程,如上图所示。图所示。递推过程:每调用一次自身,把当前参数(形参、局部变递推过程:每调用一次自身,把当前参数(形参、局部变量、返回地址等)压入栈,直到递归结束条件成立。量、返回地址等)压入栈,直到递归结束条件成立。回归过程:然后从栈中弹出当前参数,直到栈空。回归过程:然后从栈中弹出当前参数,直到栈空。递归算法设计简单,解决同一问题,使用递归算法消耗的递归算法设计简单,解决同一问题,使用递归算法消耗的机时和占据的内存空间
28、要比使用非递归算法大。机时和占据的内存空间要比使用非递归算法大。使用递归算法必须要满足以下的递归条件:使用递归算法必须要满足以下的递归条件:(1)存在递归结束条件及结束时的值;)存在递归结束条件及结束时的值;(2)能用递归形式表示,且递归向终止条件发展。)能用递归形式表示,且递归向终止条件发展。8.8 应用举例应用举例8.8.1 查找问题查找问题例例8.11使用顺序查找法,在一组数中查找某给定的数使用顺序查找法,在一组数中查找某给定的数x 算法设计:算法设计:设一组数据存放在数组设一组数据存放在数组a(1)-a(n)中,待查找的数据放在中,待查找的数据放在x 中,把中,把x与与a数组中的元素从
29、头到尾一数组中的元素从头到尾一 一进行比较查找。用一进行比较查找。用变量变量p表示表示a数组元素下标,数组元素下标,p初值为初值为1,使,使x与与a(p)比较,如比较,如果果x不等于不等于a(p),则使,则使p=p+1,不断重复这个过程;一旦,不断重复这个过程;一旦x等等于于a(p)则退出循环;另外,如果则退出循环;另外,如果p大于数组长度,循环也应该大于数组长度,循环也应该停止,则可由以下语句来实现它。停止,则可由以下语句来实现它。For P=1 to n IF a(p)=x then Exit For Next P下面编写一查找函数下面编写一查找函数Find(),若找到,若找到x则返回下标
30、值,找不则返回下标值,找不到返回到返回0Private Function Find(a()As Single,x As Single)As Integer Dim n%,p%,m%m=Lbound(a)n=Ubound(a)For P=m to n IF a(p)=x then Exit For Next P If pn then p=0 Find=pEnd Function 例例8.12使用折半查找法,在一批有序数列中查找给定的数使用折半查找法,在一批有序数列中查找给定的数x。分析:分析:设设n个有序数(从小到大)存放在数组个有序数(从小到大)存放在数组a(1)-a(n)中,中,要查找的数为
31、要查找的数为x。用变量。用变量bot、top、mid 分别表示查找数据范分别表示查找数据范围的底部(数组下界)、顶部(数组的上界)和中间,围的底部(数组下界)、顶部(数组的上界)和中间,mid=(top+bot)/2,折半查找的算法如下:,折半查找的算法如下:(1)x=a(mid),则已找到退出循环,否则进行下面的判断;,则已找到退出循环,否则进行下面的判断;(2)xa(mid),x必定落在必定落在mid+1和和top的范围之内,即的范围之内,即bot=mid+1;(4)在确定了新的查找范围后,重复进行以上比较,直到)在确定了新的查找范围后,重复进行以上比较,直到找到或者找到或者bota(p)
32、and p=0 (3)要注意函数的值域。)要注意函数的值域。如:如:exp(23773)的值就超出实数在计算机中的表示范围。的值就超出实数在计算机中的表示范围。8.9 常用内部函数常用内部函数 1.数学函数数学函数 常用的数学函数见常用的数学函数见P121表表8-1所示所示 Abs(N)Cos(N)Sin(N)Exp(N)Log(N)Sqr(N)Rnd(N)Sgn(N)说明:说明:(1)在三角函数中的自变量是以弧度为单位。在三角函数中的自变量是以弧度为单位。如:如:sin300 sin(3.14159/180*30)(2)Rnd函数返回函数返回0 1(包括(包括0和不包括和不包括1)之间的双精
33、度随机)之间的双精度随机数。数。若要产生若要产生1-100的随机整数:的随机整数:Int(Rnd*100)+1 提问:怎样产生提问:怎样产生N,M区间的随机数?区间的随机数?2.转换函数转换函数 常用的转换函数见常用的转换函数见p123表表8-4所示所示 说明:说明:(1)要区别两个取整函数)要区别两个取整函数int()和和fix()Fix(N)为截断取整,即去掉小数后的数。为截断取整,即去掉小数后的数。Int(N)不大于不大于N的最大整数。的最大整数。N0与与int(N)相同,当相同,当N0时,时,int(N)与与fix(N)-1相等。相等。例如:例如:Fix(9.59)=9,Int(9.5
34、9)=9 Fix(-9.59)=-9,Int(-9.59)=-10 思考:如何实现四舍五入取整?思考:如何实现四舍五入取整?(2)Asc(Abcd)值为:值为:65 (只取首字母的(只取首字母的Ascii值)值)(3)Val(abc123)值为:值为:0,Val(1.2sa10)值为值为1.2 注意:注意:Val()函数只将最前面的数字字符转换为数值。函数只将最前面的数字字符转换为数值。3.字符串操作函数字符串操作函数 常用的字符串函数见常用的字符串函数见p122表表8-2 说明:说明:如果返回是字符型,则函数后有如果返回是字符型,则函数后有“$”字符。当然一般也字符。当然一般也可以不写,习惯
35、都写上。可以不写,习惯都写上。例如:例如:len(This is a book!)15 Left$(ABCDEFG,3)ABC Mid$(ABCDEFG,2,3)BCD InStr(2,“ABCDEFGEF”,“EF”)5(第一次出现的位置)(第一次出现的位置)4.日期、时间函数日期、时间函数 常用的日期时间函数见常用的日期时间函数见P122表表8-3 掌握:掌握:Time()Date()Now()Year()Month()Day()5.格式输出函数格式输出函数 使用格式使用格式 Format$(表达式,“格式字符串”)功能:按用户指定格式返回表达式,功能:按用户指定格式返回表达式,常常用在常
36、常用在print方方法中。法中。使用形式:使用形式:print Format$(表达式(表达式,“格式字符格式字符串串”)表达式:可以是数值、日期或字符串型表达式。表达式:可以是数值、日期或字符串型表达式。格式字符串:表示输出表达式时采用的输出格式。不格式字符串:表示输出表达式时采用的输出格式。不同数据类型所采用的格式字符串是不同的。同数据类型所采用的格式字符串是不同的。(1)数值型数据格式化(见表)数值型数据格式化(见表8-1)注意注意:对于符号:对于符号:0与与#,当数值的实际位数比格式控,当数值的实际位数比格式控制给定的位数多时,系统将按四舍五入返回给定的位数。制给定的位数多时,系统将按四舍五入返回给定的位数。如:如:Format(3.14159,“#.#”),其值为其值为 3.142(2)日期和时间型数据格式化)日期和时间型数据格式化 缺省日期格式为:缺省日期格式为:“mm/dd/yy”,时间格式:时间格式:“hh:mm:ss”(3)字符串类型数据格式化)字符串类型数据格式化6.其它函数其它函数 除上面常用的四类函数,除上面常用的四类函数,VB中还有测试函数、与中还有测试函数、与文件操作有关的函数及其它函数。文件操作有关的函数及其它函数。