配色: 字号:
《Visual Basic程序设计教程(第3版)》第7章 过程
2023-05-25 | 阅:  转:  |  分享 
  
本章主要内容 第7章 过 程退出通用过程参数传递嵌套调用与递归调用过
程、变量的作用域程序举例多窗体与Sub Main过程Function过程Sub过程 前面已经使用了很多过程,
例如 按钮事件过程、窗体装载事件过程等。 过程是完成某种特殊功能的一组独立的程序代
码。 VB应用程序是由过程组成的。两大类过程: 事件过程
Sub过程 通用过程
Function过程 事件过程是VB应用
程序的主体 通用过程是独立于事件过程之外,可供其他过程调用的程序段Private Sub Command1_Cli
ck() …… End SubPrivate Sub Form_Load() …… End Sub 过程
有两个重要作用:一是把一个复杂的任务分解为若干个小任务,可以用过程来表达,从而使任务更易理解,更易实现,将来更易维护;二是代码重用
,使同一段代码多次复用。过程的作用 过程调用示例 把相同的任务编成相对独立的“子过程”
当事件过程需要执行这个任务时,可使用调用语句(如Call)实行调用。子过程执行完后,会返回事件过程中调用语
句Call的后续语句继续执行 通用过程又分为Sub(子程序)过程和Function(函数)过程 7.1 通
用过程 例7.1 Sub过程的示例 Private Sub Form_Load() Show Ca
ll mysub1(30) Call mysub2 Call mysub2 Call m
ysub2 Call mysub1(30) End Sub 程序运行结果 Private S
ub mysub1(n) Print String(n, "")End SubPrivate Sub mysub2()
Print ""; Tab(30); ""End Sub 7.1.1 Sub过程例7.1参数n无参数 1. S
ub过程定义格式: [Private|Public | Static] Sub 过程名([参数表])
语句块 [Exit Sub] End Sub 2. 调用Sub过程有以下两种方法:
(1) 使用Call语句。格式: Call 过程名([实参表]) (2)
直接使用过程名,格式: 过程名[实参表] 例如,调用名为mysub1的过程: C
all mysub1(30) mysub1 30 Sub过程的定义和调用 因为计算5!和10!
都要用到阶乘n! (n!=1×2×3×…×n) 所以把计算n!编成Sub过程 采用Print直接在窗体上输出结
果,程序代码:Private Sub Form_Load() Show Dim y As Long, s As Lon
g Call Jc(5, y) s = y Call Jc(10, y)Print "5! + 10! ="
; s + yEnd Sub程序运行结果: 5! + 10! = 3628920Private Sub Jc(n As In
teger, t As Long) Dim i As Integer t = 1
For i = 1 To n t = t i Next iEnd
Sub 注意参数n及t的调用情况 例7.2 计算5!
+ 10!例7.2 1. Function过程定义格式:[Private | Public | Static] Function
函数名([参数表]) [As 数据类型] 语句块 [函数名=表达式] [Exit Function
] End Function 2. Function过程的调用方法: 一般采用直接调用的方法 像使用VB内部函数一样
,只需写出函数名和相应的参数即可 例如: s = Max(a, b) Print Max(s, c)
7.1.2 Function过程 能从过程中直接返回一个值Function max(m, n) As Sing
le ……End Function Function过程返回值 分析:在前面例7.2中,因为Sub过程名不能返回值
,所以需要在形参表中引入另一个参数t来返回阶乘值。如果改成用Function过程实现,则阶乘值可由函数名返回,因此只需要设置一个参
数n.Private Sub Form_load() Dim s As Long Show s = jc(5)
+ jc(10) ''把函数作为表达式的一部分进行调用 Print "5! + 10! ="; s
End SubFunction jc(n As Integer) As Long ''返回值的数据类型为Long Dim
i As Integer, t As Long t = 1 For i = 1 To n t =
t i Next i jc = t ''返回值赋给函数名End
Function 从中可以看到Sub过程与Function过程在定义和调用上的区别。例7.3 将例7.2中的求n!的
Sub过程改成Function过程例7.3 把求两个数中的大数编成Function过程,过程名为Max
本例采用InputBox函数输入三个数,判断出最大数后采用Print直接输出在窗体上。Private Sub Form_
Load() Show Dim a As Single, b As Single, c As Single Di
m s As Single a = Val(InputBox("输入第一个数")) b = Val(InputBox
("输入第二个数")) c = Val(InputBox("输入第三个数")) s = max(a, b) P
rint "最大数是:"; max(s, c)End SubFunction max(m, n) As Single I
f m > n Then max = m Else max = n
End IfEnd Function 例7.4 输入三个数,求出它们的最大数例7.4调用调用 通用过程(
Sub/Function)过程可以保存在两种模块中: 窗体模块(.frm)和标准模块(.bas)
(1) 在窗体模块中创建通用过程: 可以在代码窗口中直接键入来创建Sub过程
选择“工具”菜单中的“添加过程”命令 (2) 在标准模块中创建通用过程:
选择“工程”菜单中的“添加模块”命令 保存为一个独立的“.bas”文件 举例:把例7.3的函数max
(m, n)存放在模块文件中 创建通用过程的方法 Private Sub Form_Load() Show
p = 1: m = 5 Call Sub1(p) Call Sub1(m) End Sub Pri
vate Sub Sub1(x) If x > 1 Then x = x - 1 x = x 4 Pr
int x; End Sub A. 4? 15? B. 4? 16 C. 4? 4?
D. 4? 20 请回答问题:下列程序段的输出结果?思考题答案:B 在窗体上已经建立了一个
文本框Txt1和一个命令按钮Comd1,运行程序后单击命令按钮,则在文本框Txt1中显示的内容是 Dim a As Inte
ger ''模块级变量 Private Sub Comd1_Click() Dim b
As Integer, c As Integer a = 1: b = 10 Call MySub
(b, c) Txt1.Text = a + b + c End Sub Sub MySub(x, y
) y = x Mod 7 + a a = 3 End Sub A. 16
B. 17 C. 15 D. 9 请回答问题思考题答案:B 参
数传递可以实现调用过程和被调过程之间的信息交换 两种方式:按值传递和按地址传递7.2.1 形参与实参 形式参数(
简称形参) 在被调过程中的参数。出现在Sub过程和Function过程中 形式参数可以是变量名和数
组名 实际参数(简称实参) 在调用过程中的参数。过程调用时实参数据会传递给形参 形参表
和实参表中的对应变量名可以不同,但实参和形参的个数、顺序以及数据类型必须相同 7.2 参数传递 定义过程和调用过程的
示例:调用过程:Call Mysub(100, "计算机" , 1.5)定义过程:Sub Mysub(t As I
nteger,s As String,y As Single) “形实结合”是按照位置结合的,即第一个实参值(10
0)传送给第一个形参t,第二个实参值(“计算机”)传送给第二个形参s,第三个实参值(1.5)传送给第三个形参y。
数组可以作为形参出现在过程的形参表中 1. 按地址传递:关键字ByRef (或缺省)。形参与实参使用相同
的内存地址单元,这样通过过程就可以改变变量本身的值 2. 按值传递:按值传递(关键字ByVal)。传
递参数值而不是传递它的地址,在通用过程中对形参的任何操作都不会影响实参7.2.2 按地址传递和按值传递按地址传递
t = 10 示例 Call test2(t)
Print t ( t 值为5)
……t与s是 Sub test2(s As Integer)同一个变量
s = s - 5 End Sub调
用时, t 值传给 s返回时, s 值传给 t双向传值按值传递 x = 10 示例
Call test1(x) Print x
(x 值为10) ……y与x是
Sub test1(ByVal y As Integer)不同变量 y
= y + 5 End Sub调用时, x 值传给 y返回时, y 值
不传给 x单向传值 设置两个通用过程Test1和Test2,分别按值传递和按地址传递Private Sub Form_Load(
) Dim x As Integer Show x = 5 Print "执行test1前,x="
; x Call test1(x) Print "执行test1后,test2前,x="; x Call
test2(x) Print "执行test2后,x="; xEnd SubSub test1(ByVal t As In
teger) t = t + 5End SubSub test2(s As Integer) s = s
- 5End Sub 运行结果执行Test1前,x=5执行Test1后,Test2前,x=5执行Test2后,x=0 例
7.5 参数传递方式示例例7.5按值传递按地址传递 7.3.1 嵌套调用 在一个过程(Sub过程和Fun
ction过程)中调用另外一个过程。 例7.6 输入两个数n、m,求组合数Cnm 的值
Cnm = n! m!(n-m)! 7.3 嵌套调用与递归调用例7.6 Pri
vate Sub Form_Load() Show m = Val(InputBox("输入m
的值")) n = Val(InputBox("输入n的值")) If m > n The
n MsgBox "输入数据错误", 0, "检查错误" En
d End If Print "组合数是:"; Calcomb(n, m) End Sub
Private Function Calcomb(n, m) Calcomb = Jc(n) / (Jc(m)
Jc(n - m)) End Function Private Function Jc(x) t =
1 For i = 1 To x t = t i
Next i Jc = t End Function 程序中,采用了过程的嵌套调用方式。在事
件过程Form_Load()中调用了Calcomb过程,而在Calcomb过程中调用了三次jc过程。调用调用
过程的嵌套调用a=Calcomb(n,m)b=Jc(n)事件过程Form_LoadFunction过程Calcomb(n,m
)Function过程Jc(x) 一个过程调用过程本身,就称为过程的递归调用。采用递归方法来解决问题时,必须符合以下
两个条件: (1) 可以把原问题转化为较低级别的同样的问题; (2) 存在结束条件(也称边
界条件),使这种递归会在某种条件下结束 【例7.7】采用递归方法求n! (n>0)。 n! = n(n-1)!,
可以把求解n! 转化为求解(n-1)!的问题,同样,可以把求解(n-1)! 转化为求解(n-2)!的问题,……,直到n=1为止。
1!= 1是递归结束条件。 然后由1!可以求出2!,由2!可以求出3!,……,由(n-1)!可以求出n!。 定义一个通
用函数fac(n)来求n!的值,fac(n)=n!,则求n!的公式可以写成: 1
n=1 Fac(n) = n×fac(n-1) n>1 7.3.2 递归调用Private
Sub Form_Load() Dim n As Integer, m As Double Show
n = Val(InputBox("输入1~15之间的整数")) If n < 1 Or n > 15
Then MsgBox "错误数据", 0, "检查数据" End
End If m= fac(n) Print n; "!= "; mEnd SubPrivate
Function fac(n) As Double If n > 1 Then fac
= n fac(n - 1) ''递归调用 Else
fac = 1 ''n=1时,结束递归
End IfEnd Function例7.7 说明:(
1)求解递归问题的过程分两个阶段:“递推”和“回归”。在逐层递推时,若遇到递归的结束条件,便按原路径逐层返回;
(2)某次递推并不能立即得到fac(n)的值,而是一次又一次地进行递推,直到fac(1)时才有确定的值,然后在逐层回归中依次算出
fac(2)、fac(3)、fac(4)的值。 递归调用过程(n=4)

递归结束条件是余数为0 Private Sub Form_Load() Show
m = Val(InputBox("输入m的值")) n = Val(InputBox("输入n的值"
)) Print m; "和"; n; "的最大公约数是:"; gys(n, m) End Sub P
rivate Function gys(n, m) p = n Mod m If p = 0
Then ‘结束条件 gys = m Else
gys = gys(m, p) ''m→n,p→m,再调用 End If En
d Function 例7.8 用递归方法求两个正整数n和m的最大公约数例7.8 7.4.1
代码模块的概念 应用程序包括窗体文件(.frm)、模块文件(.bas)和类模块(
.cls) 7.4 过程、变量的作用域 模块级和全局级。根据使用的关键字不同,过程有不同的作用域。
(1)模块级过程。在窗体模块或标准模块中用关键字Private定义的过程,其作用域仅仅是其所在的模块(窗体模块或标准模块),在
其他模块中无效。 (2)全局级过程。在窗体模块或标准模块中用关键字Public(或省略关键字)定义的过程,其作用域是整
个应用程序的所有模块。 当全局级过程是在窗体模块中定义时,在其他模块中调用时要指出窗体模块的名字,即:“窗体模块
名.全局级过程名[(实参表)]”;当全局级过程是在标准模块中定义时,在其他模块中可以直接调用,即:“全局级过程名[(实参表)]”。
7.4.2 过程的作用域 局部变量、模块级变量和全局变量 1. 局部变量
在一个过程内部用Dim或Static声明的变量称为局部变量。只能在本过程中有效。在一个窗体中,不同过程中定义的局部变量可以
同名。例如,在一个窗体中定义 Private Sub Command1_Click()
Dim Sum As Integer Sum = 1
. . . End Sub Private Sub Command
2_Click() Dim Sum As Single Sum
= 1.5 . . . End Sub 这两个同名变量Sum没有任
何联系 7.4.3 变量的作用域它们互不相识 模块级变量可以在一个窗体的不同过程中使用 在窗体模块的声明部分中声
明该变量 Dim n As Integer Private Sub Command1_Click() n=1
… End Sub Private Sub Command2_Click() n=n+1
… End Sub 如果用Private或Dim来声明,则该变量只能在本窗体(或本模块)中有效,在其他窗
体或模块中不能引用该变量。 以Public声明的变量,允许在其他窗体和模块中引用 2. 模块级变量
全局变量可以被应用程序中任何一个窗体和模块直接访问。 全局变量要在标准模块文件(.bas)中的声明部分用Globa
l或Public语句来声明。 格式: Global 变量名 As 数据类型
Public 变量名 As 数据类型 3. 全局变量 生存期,即变量能够保持其值的时间
动态变量和静态变量(见书P157) 1. 动态变量 程序运行进入变量所在的过程时,才为变量分配内
存单元,退出过程时,该变量占用的内存单元自动释放,其值消失。当再次进入该过程时,所有的动态变量将重新初始化。 使
用Dim关键字在过程中声明的局部变量属于动态变量 2. 静态变量 退出该过程时变量值被保留,即变量所占的内
存单元没有释放。当以后再次进入该过程时,原来的变量值可以继续使用 使用Static关键字在过程中声明的局部变量属
于静态变量 7.4.4 变量的生存期 Private Sub …… Dim a As Integer ‘a是
动态变量 Static b As Integer ‘b是静态变量 x = a + 1 ‘
每次进入过程时, ‘ a值为0 y = b
+ 1 ‘每次进入过程时, ‘
b保持上次的值 ……End Sub 7.5.1 多窗体处理 在多
窗体程序中,每个窗体可以有自己的界面和程序代码,完成不同的操作 1. 添加窗体
通过“工程”菜单中的“添加窗体”命令来实现 2. 删除窗体 选择“工程”菜单中的“移
除”命令 3. 保存窗体 选择“文件”菜单中的“保存”或“另存为”命令
4. 设置启动窗体 在“工程属性”对话框中设置 7.5 多窗体与Sub Main过程
Load语句:把一个窗体装入内存 UnLoad语句:清除内存中指定的窗体 Sh
ow方法:显示一个窗体 Hide方法:隐藏窗体。即不在屏幕上显示,但仍在内存中,因此它与UnLoad的作用是不
一样的 例如: Load Form1
Form1.Show 注意:语句和方法的使用格式不同。 调用对象的方法:
对象名.方法名 5. 有关语句和方法 创建3个窗体和1个标准模块 (1)主窗体
(Form1) 本窗体上建立了“输入数据”(Command11) 、“计算” (Command12)和
“结束”(Command13)三个命令按钮 设置为启动窗体例7.10 多窗体应用示例—— 计算两数之和及
积例7.10主窗体输入两个数计算定义全局变量启动窗体Private Sub Command11_Click()
‘主窗体“输入数据”按钮
‘单击后进入“输入数据”窗体 Form1.Hide
''隐藏主窗体 Form2.Show
''显示“输入数据”窗体End SubPrivate Sub
Command12_Click() ‘主窗体“计算”按钮
‘单击后进入“
计算”窗体 Form1.Hide ''隐
藏主窗体 Form3.Show ''显示“
计算”窗体End SubPrivate Sub Command13_Click() ''主窗体“结束”按钮
Unload Form1 Unload Form2 Unload Form3
EndEnd Sub先卸载所有已打开的窗体 (2)“输入数据”窗体(Form2) 这是在主窗体上单
击了“输入数据”按钮后弹出的窗体,用于输入运算数x和y。窗体上建立了2个文本框(Text21和Text22)和1个“返回”命令按钮
(Command21) Private Sub Command21_Click() ‘“输入数据”窗体的“
返回”按钮 X = Val(Text21.Text) Y = Val(Text22.Text)
Form2.Hide
''隐藏“输入数据”窗体 Form1.Show
''显示主窗体 End Sub (3)“计算”窗体(Form3)
这是在主窗体上单击了“计算”按钮后弹出的窗体。窗体上建立了1个文本框和2个命令按钮,如图7.10所示。用户可以单击“加法”(C
ommand31) 或“乘法”(Command32)命令按钮,使之按要求进行计算。Private Sub Command31_Cl
ick() ‘“计算”窗体的“加法”按钮 Text31.Text = X + YEnd SubPr
ivate Sub Command32_Click() ‘“计算”窗体的“乘法”按钮 Text31
.Text = X YEnd SubPrivate Sub Command33_Click() ''“计算
”窗体的“返回”按钮 Form3.Hide
''隐藏“计算”窗体 Form1.Show
''显示主窗体End Sub (4)标准模块(Modulel)
由于在各窗体之间需要使用公共变量来传送数据,所以建立一个标准模块Modulel,对用到的全局变量X和Y进行声明
运行程序后,首先显示主窗体。在主窗体上,用户可通过“输入数
据”和“计算”两个按钮来选择进入不同的窗体,例如单击“输入数据”按钮,则主窗体消失,显示“输入数据”窗体。在“输入数据”窗体或“计
算”窗体上,单击“返回”按钮,又可以隐藏当前窗体和重现主窗体 程序一般从启动窗体的Form_Loa
d过程开始执行 有时在程序启动时不加载任何窗体,而是首先执行一段程序代码,此时可把要
执行的程序代码放在Sub Main过程中,并指定Sub Main为“启动对象”。 应用程序在运行时会先执行Su
b Main过程 Sub Main过程存放在标准模块中。在一个工程中只能有一个Sub Main过程
设置Sub Main过程为“启动对象”的方法:在“工程属性”对话框的“通用”选项卡中,从
“启动对象”下拉列表框中选中“Sub Main” (见 )7.
5.2 Sub Main过程例7.11 例7.12 输入一个十进制正整数,将其转换成二进制数、八进制数和十六
进制数。 (1)分析:模仿十进制正整数转换成二进制数的方法(即“除2取余”),采用逐次“除r取余”法(r为2,8或16),即用r
去除d(十进制数)取余数,商赋给d,如此不断地用r去除d取余数,直至商为0为止,将每次所得的余数逆序排列(以最后余数为最高位),即
得到所转换的r进制数。 将进制转换处理段定义为Function过程,过程名为fntran,并设置两个参数,将参数d设置为按值
传递(ByVal)方式。 7.6 程序举例Private Sub Command1_Click() ‘“转换
” Dim d As Long d = Val(Text1.Text) Text2.Text = fntran(d,
2) ''调用函数fntran,转换为二进制数 Text3.Text = fntran(d, 8)
''调用函数fntran,转换为八进制数 Text4.Text = fntran(d, 16) ''调用函数fntran,转
换为十六进制数End SubFunction fntran(ByVal d As Long, r As Integer) As S
tring Dim t As String, n As Integer t = "" Do While d > 0
''直到商为0 n = d Mod r ''取余数
d = d \ r ''求商 If n > 9 Then
''超过9转换成对应的A~F十六进制数表示形式 t = Chr(n + 55) & t ''换码为
字母,反序加入 Else t = n & t ''反序加入 End
If Loop fntran = tEnd Function例7.12 例7.13 将判断一个数是否为素
数编成一个函数,然后通过调用该函数求500~1000数中的所有素数,把这些素数显示在列表框中。 (1)分析:素数也称
质数,就是只能被1和它本身整除,而不能被其他整数整除的整数。例如 2,3,5,7 是质数,而 4,6,8,9 则不是。判断某数m是
否是素数的算法是:对于m,从k = 2,3,4,……,m-1依次判别能否被k整除,只要有一个能整除,m就不是素数,否则m是素数。
 例7.13Function FnPrime(m As Integer) As Boolean Dim k As
Integer, f As Boolean f = True
''设置f来表示判断状态,初值为True For k = 2 To m - 1
''从k = 2,3,4,……,m-1依次判断 If m Mod k = 0 Then ''判m
是否能被k(2~m-1中的一个数)整除 f = False
''如m能被k整除,则置f为Flase End If Next k FnPrime
= f ''返回函数值End FunctionPrivate
Sub Command1_Click() ''"求素数"按钮 Dim t As Integer List
1.Clear ''清除列表框中的内容 For
t = 500 To 1000 If FnPrime(t) Then ''调用函数,根据t是否素
数返回函数值真或假 List1.AddItem t ''若是素数,则存入列表框中
End If Next t Label1.Caption = "500~1000数中共有" & List1
.ListCount & "个素数"End Sub 分析:为了得到一个圆大小变动的动画效果,先在某一位置上绘制一个圆,显示
一段时间(延时)后抹除,接着在下一位置上依此处理,直到指定位置为止。    延时:利用 Timer函数,该函数返回系统时
钟从午夜开始计算的秒数(带两位小数)    Private Sub Delay(d)     ‘功能:延时d秒
t = Timer() + d Do While Timer() < t    ''利用空循环实现延迟
Loop End Sub例7.14 绘制一个圆,使之从小变大,再从大变小例7.14系统提供的当前时间数
设定的时间数了解Timer的作用抹除方法:采用底色(背景色)来掩盖图形采用Circle方法可以画一个圆 Private Sub F
orm_Load() Show Form1.BackColor = QBColor(15)
''设置背景颜色 Call Pict(30, 1600, 30)
''从小变大 Call Pict(1600, 30, -30)
''从大变小End SubPrivate Sub Pict(a, b, c)
‘显示→延迟→抹除 For i = a To b Step c
‘在i位置上处理 Call Plot(i, 4)
''显示圆 Delay 0.1
''延迟0.1秒 Call
Plot(i, 15) ''抹除 Next i
End SubPrivate Sub Plot(r, clr) Form1.Circle (2400, 1600),
r, QBColor(clr) ''画圆End Suba及b为半径的开始值及结束值,c为每次半径增量r、clr
分别为圆半径和颜色值 在窗体上建立3个文本框,使之以不同效果显示文字“2008年世界奥运会在中国北京市隆重举行” 第一个文本框Text1从左到右逐字显示,直到把整行文字显示出来; 第二个文本框Text2使文字从左到右作水平移动; 第三个文本框Text3以闪动方式显示文字 设计界面 运行界面 例7.15 动态文字例7.15Dim txt As String, n As Integer, k As IntegerPrivate Sub Form_Load() n = 0 txt = "2008年世界奥运会将在中国北京市举行" k = Len(txt) Text1.ForeColor = RGB(255, 0, 0) ''红色 Text2.ForeColor = RGB(0, 0, 0) ''黑色 Text3.ForeColor = RGB(0, 0, 255) ''蓝色End SubPrivate Sub Timer1_Timer() n = n + 1 ‘模块级变量n是关键参数 If n <= k Then Text1.Text = Left(txt, n) ‘逐次取n个汉字,n每次加 1 Text2.Text = Space(2 (k - n)) + Left(txt, n) Else n = 0 Text1.Text = "" Text2.Text = "" End If If n Mod 2 = 0 Then ‘以2个定时时间为1个周期,显示及清除交替进行 Text3.Text = txt ''n为偶数时显示 Else Text3.Text = "" ''n为奇数时清除 End IfEnd Sub每次减少2空格1个汉字占2个字符位置每次增加1个汉字
献花(0)
+1
(本文系大高老师首藏)