第六章 过 程
6.1 过程概述在 VB5.0/6.0中,除了系统提供的内部函数过程和事件过程外,用户可自定义过程下列四种过程:
▲,Sub” 保留字开始的为子过程
▲,Function” 保留字开始的为函数过程
,Property”保留字开始的为属性过程
,Event”保留字开始 的事件过程在 VB中,的 Sub子过程分为:事件过程和通用过程
6.2.1 事件过程
( 1) 窗体事件的语法
Private Sub Form_事件名 (参数列表 )
<语句组>
End Sub
( 2)控件事件的语法
Private Sub 控件名 _事件名 (参数列表 )
<语句组>
End Sub
6.2 Sub过程子过程的定义形式如下:
[Public|Private][Static] Sub 子过程名 ([形参表 ])
<局部变量或常数定义 >
<语句组>
[Exit Sub]
<语句组>
End Sub
6.2.2 通用过程( Sub过程)的定义说明:
( 1) 子过程名:命名规则与变量名规则相同 。 子过程名不返回值,而是通过形参与实参的传递得到结果,调用时可返回多个值 。
( 2) 形式参数列表:形式参数通常简称,形参,,仅表示形参的类型,个数,位置,定义时是无值的,只有在过程被调用时,虚实参结合后才获得相应的值 。
( 3) 过程可以无形式参数,但括号不能省 。
( 4) 参数的定义形式:
[ByVal| ByRef]变量名 [( )][As 类型 ][,… ],
ByVal表示当该过程被调用时,参数是按值传递的;缺省或
ByRef表示当该过程被调用时,参数是按地址传递的。
例 6.1编一个交换两个整型变量值的子过程。
Private Sub Swap( X As Integer,Y As Integer)
Dim temp As Integer
Temp=X,X=Y,Y=Temp
End Sub
6.2.3 子过程的建立通过,工具,菜单中的,添加过程,命令,出现,添加过程,对话框 ( 见图 6.1),选择过程类型 ( 子过程,函数,属性,事件 ) 及作用范围 ( 公有的 Public,私有的 Private),单击确定后得到一个过程或函数定义的结构框架 ( 模板 ),如:
Public Sub Sort( )
……
End Sub
6.2.4 过程的调用
1 子过程的调用子过程名 [参数列表 ]
或 Call 子过程名 (参数列表 )
2 函数过程的调用变量名 = 函数过程名 ([参数列表 ])
说明:
(1)参数列表 称为实参或实元,它必须与形参保持个数相同,位置与类型一一对应。
(2)调用时把实参值传递给对应的形参。其中值传递(形参前有 ByVal说明)时实参的值不随形参的值变化而改变。而地址传递时实参的值随形参值的改变而改变。
(3)当参数是数组时,形参与实参在参数声明时应省略其维数,但括号不能省。
(4)调用子过程的形式有两种,用 Call关键字时,实参必须加圆括号括起,反之则实参之间用“,”分隔。
例:调用上面定义的 Sawp子过程的形式:
Swap a,b
Call Swap(a,b)
Private Sub CmdOK()
……
Call Swap(a,b)
……
Swap a,b
End Sub
Private Sub Swap(x%,y%)
Dim temp%
Temp=X
X=Y
Y=Temp
End Sub
过程调用的执行过程:
6.3 Function过程
Visual Basic函数分为内部函数和外部函数,外部函数是用户根据需要用 Function关键字定义的函数过程,与子过程不同的是函数过程将返回一个值 。
6.3.1 函数过程( Function过程)的定义
[Public|Private][Static]Function 函数名 ([<参数列表 >])[As<类型 >]
<局部变量或常数定义 >
<语句块 >
函数体 [函数名 =返回值 ]
[Exit Function]
<语句块 >
[函数名 =返回值 ]
End Function
函数体说明:
( 1) 函数名:命名规则与变量名规则相同 。 但不能与系统的内部函数或其它通用子过程同名,也不能与已定义的全局变量和本模块中模块级变量同名 。
( 2) 在函数体内,函数名可以当变量使用,函数的返回值就是通过对函数名的赋值语句来实现的,在函数过程中至少要对函数名赋值一次 。
( 3) AS类型:是指函数返回值的类型,若省略,则函数返回变体类型值 ( Variant) 。
( 4) Exit Function:表示退出函数过程,常常是与选择结构
( If或 Select Case语句 ) 联用,即当满足一定条件时,退出函数过程 。
( 5)形参数列表:形参的定义与子过程完全相同。
例 6.2
6.3.2 函数的调用调用形式如下:
函数名 ( 实参列表 )
说明:
在调用时实参和形参的数据类型,顺序,个数必须匹配 。 函数调用只能出现在表达式中,其功能是求得函数的返回值 。
例 6.3
6.4 过程之间参数的传递
Visual Basic中不同模块 ( 过程 ) 之间数据的传递有两种方式:
通过过程调用实参与形参的结合实现;
使用全局变量来实现各过程中共享数据。
6.4.1 形式参数与实际参数
1、形式参数是指在定义通用过程时,出现在 Sub或 Function语句中的变量名后面园括号内的数,是用来接收传送给子过程的数据,形参表中的各个变量之间用逗号分隔。
2,实际参数实际参数是指在调用 Sub或 Function过程时,写入子过程名或函数名后括号内的参数,其作用是将它们的数据 ( 数值或地址 ) 传送给 Sub或 Function过程与其对应的形参变量 。
实参可由常量,表达式,有效的变量名,数组名 ( 后加左,
右括号,如 A()) 组成,实参表中各参数用逗号分隔 。
6.4.2 参数传递(虚实结合)
参数传递指主调过程的实参(调用时已有确定值和内存地址的参数)传递给被调过程的形参,参数的传递有两种方式:按值传递、按地址传递。形参前加,ByVal”关键字的是按值传递,缺省或加,ByRef”关键字的为按地址传递。
传址与传值传址,形参得到的是实参的地址,当形参值的改变同时也改变实参的值。
传值,形参得到的是实参的值,形参值的改变不会影响实参的值。
Sub Swap2(x%,y%)
Dim Temp%
Temp = x,x = y,y = Temp
End Sub
Sub Swap1(ByVal x%,ByVal y%)
Dim Temp%
Temp = x,x = y,y = Temp
End Sub
例 6.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="; b
End Sub
程序调用执行过程:
值传递的执行过程如下图:
通过地址传递数据的执行过程
6.5 数组作过程的参数
Visual Basic允许把数组作为实参传送到过程中 。 数组作参数是通过传地址方式传送 。 在传送数组时,除遵守参数传送的一般规则外,还应注意以下几点:
1,为了把一个数组的全部元素传送给一个过程,应将数组名分别写入形参表中,并略去数组的上下界,但括号不能省略 。
Private Sub Sort( a() As single)
……
End Sub
其中形参,a( )”即为数组 。
2,被调过程可通过 Lbound和 Ubound函数确定实参数组的上,
下界 。
3,当用数组作形参时,对应的实参必须也是数组,且类型一致 。
4,实参和形参结合是按地址传递,即形参数组和实参数组共用一段内存单元 。
例如:定义了实参数组 b(1 to 8),给它们赋了值,调用 Sort()
函数过程的形式如下:
Sort b() 或 Call Sort(b() )
实参数组后面的括号可以省略,但为便于阅读,建议一般不要省略为好。
调用时形参数组 a和实参数组 b虚实结合,共用一段内存单元,
如图 6-7所示 。 因此在 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 改写上章例 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 k=2 to n\2
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 n\2 step 2 ' 让 n1从 3开始分解
n2=n-n1 ' 求得 n2
‘ 如果 n1和 n2都是素数,则打印输出
If prime(n1) And prime(n2) then
Print n & "=" & n1 & "+" & n2
Exit For ' 结束循环
End if
Next n1
End Sub
For i = 1 To n - 1
p = i
For j = i + 1 To n
If a(p) > a(j) Then p = j
Next j
temp = a(i)
a(i) = a(p)
a(p) = temp
Next i
选择法排序(升序)的 VB程序,
如何写一个排序的 VB过程?
补充例题 2:排序子过程
Private Sub Sort( a( ) As single)
Dim i%,j%,p%,n%,temp!
n=Ubound(a)
For i = 1 To n - 1
p = i
For j = i + 1 To n
If a(p) > a(j) Then p = j
Next j
temp = a(i)
a(i) = a(p)
a(p) = temp
Next I
End Sub
6.6 过程的嵌套和递归调用
6.6.1 过程的嵌套在程序中调用一子过程,而在子过程中又调用另外的子过程,这种程序结构称为 过程的嵌套 。 过程的嵌套调用执行过程如 下图:
6.6.2 过程的递归调用用自身的结构来描述自身,称递归。例如,对阶乘的定义:
)!2()1()!1(
)!1(!


nnn
nnn
Visual Basic允许在一个 Sub子过程和 Function过程的定义内部调用自己,即递归 Sub子过程和递归 Function函数。
例 6.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 If
End Function
Private Sub Form_Click()
Print,fac(4)=”; fac(4)
End Sub
程序的递归过程:
说明:
递归处理一般用栈来实现,分递推和回归两个过程,如上图所示 。
递推过程:每调用一次自身,把当前参数 ( 形参,局部变量,返回地址等 ) 压入栈,直到递归结束条件成立 。
回归过程:然后从栈中弹出当前参数,直到栈空 。
递归算法设计简单,解决同一问题,使用递归算法消耗的机时和占据的内存空间要比使用非递归算法大。
使用递归算法必须要满足以下的递归条件:
( 1)存在递归结束条件及结束时的值;
( 2)能用递归形式表示,且递归向终止条件发展。
*6.7 过程的可选参数与不定数量参数
6.7.1 可选参数( Optional)
概念:
当然用户在自定义子过程或函数时也可以定义可选参数 。 要指定某个形参为可选参数,这就是在形参变量前加入 Optional关键字即可 。
如果一个过程某个形参为可选参数,则在调用此过程时可以不提供对应于这个形参的实参,
若一个过程有多个形参,当它的一个形参被设定为可选参数,则这个参数之后面的其它参数也必是可选的,并且要用 Optional 关键字来声明 。
例 6.9 可选参数的使用
Private Sub Nadres(x As String,Optional y
As String)
Text1.text= x
Text2.text= y
End Sub
Private Sub Command1_Click ()
Dim strName As String
Dim strAddress As String
strName = "张 玉 "
strAddress = "杭州市学院路 85号 "
提供了两个参数 。
Call Nadres (strName,strAddress)
End Sub
说明:
1,检测可选参数是否被省略使用 VB的内部函数 IsMissing(可选参数名) 来测试过程被调用时,某个参数是否被省略。如果可选参数被省略了,则返回 True,未省略,返回 False。
注意:若可选参数是其它类型,则 IsMissing()总是返回 False,不能起到检测的作用。
例:
Private Sub Nadres(x As String,Optional y
As Variant)
Text1.Text = x
If Not IsMissing(y) Then
Text2.Text = y
End If
End Sub
2,指定可选参数的缺省值也可以给可选参数指定缺省值 。 在下例中,如果未将可选参数传递到函数过程,则返回一个缺省值 。
Sub Nadres(x As String,Optional y As
String= "杭州市学院路 85号 ")
Text1.text= x
Text2.text= y
End Sub
Private Sub Command1_Click ()
Dim strName As String
strName = "张 玉 " ' 未提供第二个参数 。
Call Nadres (strName)
End Sub
6.7.2 使用不定数量的参数一般说来,过程调用中的参数个数应等于过程说明的参数个数 。 可用 ParamArray 关键字指明,过程将接受任意个数的参数 。
其定义形式如下:
Sub 过程名 (ParamArray 数组名 ())
Function 函数名 (ParamArray 数组名 ()) As 数据类型例 6.10
6.8 应用举例
6.8.1 查找问题例 6.11 使用顺序查找法,在一组数中查找某给定的数 x
算法设计:
设一组数据存放在数组 a(1)---a(n)中,待查找的数据放在 x
中,把 x与 a数组中的元素从头到尾一 一进行比较查找。用变量 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则返回下标值,找不到返回 0
Private 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 p>n then p=0
Find=p
End Function
例 6.12
使用折半查找法,在一批有序数列中查找给定的数 x。
分析,设 n个有序数 ( 从小到大 ) 存放在数组 a(1)----a(n)中,
要查找的数为 x。 用变量 bot,top,mid 分别表示查找数据范围的底部 ( 数组下界 ),顶部 ( 数组的上界 ) 和中间,
mid=(top+bot)/2,折半查找的算法如下:
( 1) x=a(mid),则已找到退出循环,否则进行下面的判断;
( 2) x<a(mid),x必定落在 bot和 mid-1的范围之内,即
top=mid-1;
( 3) x>a(mid),x必定落在 mid+1和 top的范围之内,即
bot=mid+1;
( 4) 在确定了新的查找范围后,重复进行以上比较,直到找到或者 bot<=top。
6.8.2 插入问题例 6.13 把一个给定数插到有序数列中,插入后数列仍然有序分析:设 n个有序数 ( 从小到大 ) 存放在数组 a(1)—a(n)中,
要插入的数 x。 首先确定 x插在数组中的位置 p; ( 可由以下语句实现 )
ReDim Preserve a(n + 1) '让数组长度增加 1,
以便存放插入的数
p=1
do while x>a(p) and p<=n
p=p+1
loop
a(p)—a(n)元素向后顺移一个位置以空出 a(p)元素放入 x.。
( 可由以下语句实现 )
for i=n to p step-1
a(i+1)=a(i)
next i
a(p)=x
将其写成一插入函数 ( 教材 Pg.154 )