第六章 过程
( 5学时)
6.1 函数过程的定义与调用
6.2 子过程的定义与调用
6.3 参数传递
6.4 变量、过程的作用域
6.5 递归
6.6 常用算法(三)
6.7 重点和难点除了系统提供的内部函数过程和事件过程外,用户可自定义过程:
,Sub”保留字开始的为子过程
,Function”保留字开始的为函数过程一、函数过程的定义
1,引例 6.1
已知多边形的各条边的长度,要计算多边形的面积 。
计算 多边形面积,可将多边形分解成若干个三角形 。
计算 三角形面积的公式如下:
a
b
c
de
f
g
S1
S2
S3
)(21))()(( zyxczcycxcca r e a
x y
z定义函数过程
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)+area(e,f,g)
Print s
End Sub
2.函数过程的定义自定义函数过程有两种方法:
(1)利用“工具”菜单下的“添加过程”命令定义,生成一个函数的框架
(2)利用代码窗口直接定义函数过程形式,
Function 函数过程名 ([参数列表 ]) [As 类型 ]
局部变量或常数定义语句块函数名 = 返回值 函数过程体
[Exit Function]
语句块函数名 = 返回值
End Function
函数过程名,命名规则同变量名参数列表形式,[ByVal]变量名 [()][As 类型 ]
称为形参或哑元,仅表示参数的个数、类型,无值。
函数名 = 返回值 在函数体内至少对函数名赋值一次
[Exit Function],表示退出函数过程例 6.2同标准函数 Replace一样,定义 MyReplace( S,OldS,NewS) 函数过程当调用 MyReplace("abcdefgabcdecd","cd","3“) 时函数的返回值为 "ab3efgab3e3“
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为,abcdefgabcdecd”,Oolds为,cd”,News为 "3"
(“abcdefgabcdecd”,“cd”,“3”) 第 1 次 I=3 结果,ab3efgabcdecd”
(“ab3efgabcdecd”,“cd”,“3”) 第 2 次 I=9 结果,ab3efgab3ecd”
(“ab3efgab3ecd”,“cd”,“3”) 第 3 次 I=11 结果 "ab3efgab3e3"
3.函数过程的调用函数过程的调用同标准函数调用,参与表达式运算,形式如下:
函数过程名 ([参数列表 ])
参数列表:称为实参或实元,它必须与形参个数相同,位置与类型一一对应可以是同类型的常量、变量、表达式执行流程,
Private Sub Command1_Click()
Text1 = MyReplace(Text1,"cd","3")
ST= (“Visual Basic 程序设计教程 5.0版”
Print MyReplace(ST,"5.0","6.0")
End Sub
Function MyReplace$(s$,OldS$,NewS$)
Dim i%,lenOldS%
lenOldS = Len(OldS)
i = InStr(s,OldS)
Do While i > 0
s= Left(s,i - 1) + NewS + Mid(s,i + lenOldS)
i = InStr(s,OldS)
Loop
MyReplace = s
End Function
T e x t 1 = M y R e p l a c e (Te x t 1," c d "," 3 " )
P u b l i c F u n c t i o n M y R e p l a c e (S $,Ol d S $,New S $ ) A s S t ri n g
二、子过程函数过程的不足:
( 1)不是为了获得某个函数值,而是为了某种功能的处理,如例 1.1。
( 2) 要获得多个结果。
1.引例编写一个两个数交换的过程供多次调用。
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
2 子过程定义
Sub 子过程名 [(参数列表 )]
局部变量或常数定义语句
[Exit Sub]
语句
End Sub
3 子过程的调用子过程名 [参数列表 ]
或 Call 子过程名 (参数列表)
4.子过程与函数过程区别,
( 1)函数过程名有值,有类型,在函数体内至少赋值一次;
子过程名无值,无类型,在子过程体内不能对子过程名赋值;
( 2)调用时,子过程调用是一句独立的语句。
函数过程不能作为单独的语句加以调用,必须参与表达式运算。
( 3)一般当过程有一个函数值,使用函数过程较直观;
反之若过程无返回值,或有多个返回值,使用子过程较直观。
例 6.4 分别编一计算某级数部分和的子过程和函数过程,并调用。
级数为,精度为:
...
!
...
!2
1
2

n
xxx n ep s
!
n
x n
程序运行流程:
Private Sub Command1_Click()
Dim f1#,f2#
f1 = jishu1(2#,0.000001)
Call jishu2(f2,2#,0.000001)
Print "f1="; f1,"f2 = "; f2
End Sub
Function jishu1(x!,eps#) As Doubl

jishu=表达式
End Function
找函数名调用 jishu1
函数名带了值返回
Sub jishu2(s#,x!,eps#)

s=表达式
End Sub




⑤ 找子过程名调用 jishu2 ⑥


三,参数传递指主调过程的实参传递给被调过程的形参。
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
End Sub
Sub Swap2(x%,y%)
t% = x,x = y,y = t
End Sub
a 的地址 1 0
1 0 1 0
aa
x x
形参实参
B y V al 传值 传地址
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
5
1
1
i
iat?
8
3
2
i
ibt
调用:
Sub Command1_Click()
Dim a%(1 To 5),b%(3 To 8) …
t1# = tim(a())
t2 #= tim(b())
Print t1,t2
End Sub
使用过程注意事项:
1,确定自定义的过程是子过程还是函数过程函数过程名有值,子过程名无值。
2.过程中形参的个数和传递方式的确定过程中参数的作用是实现过程与调用者的数据通信。
(1)从主调程序获得初值,值传递。
(2)将结果返回给主调程序,地址传递。
3,实参与形参结合时对应问题个数、类型、位置、次序一一对应。
形参是值传递,对应实参可以是表达式、常量、数组元素。
形参是地址传递,对应实参只能是简单变量。
数组、记录类型、对象只能是地址传递。
实验 6.4回文数的判断中形参的确定
1,函数过程,形参一个,值传递对所判断的数字;函数名是否为回文数。
Function IsH(ByVal ss As String) As Boolean
2,子过程,形参两个,值传递对所判断的数字,地址传递是否位回文数。
Sub hui(ByVal ss As String,Tag As Boolean)
实验 6.2子过程 DeleStr(s1,s2)形参的确定
s1,要处理的字符串,从主调程序得初值,删除子串后结果在 S1中,所以地址传递。
s2删除的子串,值传递。
实验 6.3函数过程 MaxLength(s)形参的确定
S要处理的字符串,值传递。
MaxLength函数名,最长的单词长度。
四,变量、过程的作用域作用域,变量、过程随所处的位置不同,可被访问的范围。
1.过程的作用域窗体 /模块级,加 Private关键字的过程,只能被定义的窗体或模块中的过程调用。
全局级,加 Public关键字(缺省)的过程,可供该应用程序的所有 窗体和所有标准模块中的过程调用。
2,变量的作用域局部变量,在过程内用声明的变量,只能在本过程中使用。
窗体 /模块级变量,在“通用声明”段中用 Dim语句或用 Private语句声明的变量,可被本窗体 /模块的任何过程访问。
全局变量,在,通用声明”段中用 Public语句声明的变量,可被本应用程序的任何过程或函数访问。
例如在下面一个标准模块文件中不同级的变量声明:
Public Pa As integer ' 全局变量
Private Mb As string *10 ' 窗体 /模块级变量
Sub F1( )
Dim Fa As integer ' 局部变量

End Sub
Sub F2( )
Dim Fb As Single ' 局部变量

End Sub
若在不同级声明相同的变量名,系统按局部、窗体 /模块、全局次序访问如,
Public Temp As integer ' 全局变量
Sub Form_Load()
Dim Temp As Integer ' 局部变量
Temp=10 ' 访问局部变量
Form1.Temp=20 ' 访问全局变量必须加窗体名
Print Form1.Temp,Temp ' 显示 20 10
End Sub
3,静态变量局部变量声明:
Dim声明,随过程的调用而分配存贮单元,变量的初始化;过程体结束,变量的内容自动消失,存贮单元释放。
Static声明,每次调用过程,变量保持原来的值。
声明形式,Static 变量名 [AS 类型 ]
Static Function 函数过程名 ([参数列表 ]) [As 类型 ]
Static Sub 子过程名 [(参数列表 )]
过程名前加 Static,表示该过程内的局部变量都是静态变量。
例 6.9
Private Sub Form_Click()
Dim i%,isum%
For i = 1 To 5
isum = sum(i)
Print isum,
Next i
End Sub
Private Function sum(n As Integer)
Dim j As Integer
j = j + n
sum = j
End Function
Static j As Integer,结果?
1,2,3,4,5
Sub f1(x)

x=f2(xx)*3…
End Sub
Sub Command1_Click()
……
Call f1(y)
End Sub
Function f2(x)
……
f2=f3(x)+3
End Sub
Function f3(x)
……
f3=x*x
End Sub
Sub f1(x)

Call f1(y)
End Sub
过程的直接调用
Sub Command1_Click()
……
Call f1(y)
End Sub
过程的递归调用五、递归
1.递归的概念用自身的结构来描述自身就称为“递归”。例对阶乘的定义:
)!2()1()!1(
)!1(!


nnn
nnn
2,递归过程过程在自身定义的内部调用自己。
例 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



1)1f a c (*
11)f a c (
nnn
nn
Sub Command1_Click()
Print,fac(4)=”; fac(4)
End Sub
结果,fac(4)=24
递推回归在递归处理中,用栈来实现。栈中存放形参、局部变量、返回地址。
递推过程,每调用自身,当前参数压栈,直到达到递归结束条件。
回归过程,不断从栈中弹出当前的参数,直到栈空。
递归算法设计简单,但消耗的机时和占据的内存空间比非递归大。
思考:
若上述 fac函数中,If n = 1 Then fac = 1
即仅有语句,fac = n * fac(n - 1) 或 n<=0
程序运行将造成何结果?由此可见构成递归的结构如下:
递归结束条件及结束时的值;
能用递归形式表示,并且递归向终止条件发展。
例 6.11利用递归求最大公约数
0M o d)M o d,g c d (
0M o d
),g c d (
nmnmn
nmn
nm
Public Function gcd(m As Integer,n As Integer) As Integer
If (m Mod n) = 0 Then
gcd = n
Else
gcd = gcd(n,m Mod n)
End If
End Function
Private Sub Form_Click()
Print gcd(10,4)
End Sub
分析以下子过程的功能,当 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)
End Sub
n r 返回地址
12 8 2
100 8 2
显示结果 1 4 4
例 6.12 打印分形图递归常见错误:
1.递归调用出现,栈溢出,
在递归调用时,其中的参数要向终止方向收敛。
如下求阶乘的递归函数过程:
Public 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 Command1_Click()
Print,fac(5)=”; fac(5)
Print,fac(5)=”; fac(-5) ‘ 栈溢出
End Sub
1.数制转换例 6.13 将一个十进制整数 m转换成 r (2- 16)进制字符串。
方法,将 m不断除 r 取余数,直到商为零,以反序得到结果。
6.6常用算法 (三)
2.例 6.14加密和解密简单加密的思想是:
将每个字母 C加一序数 K,式子 c=chr(Asc(c)+k),
例如序数 k为 5,这时,A”?“F”,,a”?“f”,,B”?“G”…
当加序数后的字母超过,Z”或,z”则 c=hr(Asc(c)+k -26)。
解密为加密的逆过程。
3.查找
(1)顺序查找例 6.15。
顺序查找根据查找的关键值与数组中的元素逐一比较,数组可无序。
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
Key<a(mid) high=mid-1 查找区域缩小一半,继续
Key=a(mid) 找到 结束
Key>a(mid) low=mid+1 查找区域缩小一半,继续直到找到或查找区 域中无元素,
本例用递归实现 6.16
Sub birsearch(a(),low%,high%,key,index%)
(2)二分法查找要查找的数组必须有序。
思想:要查找的关键值 Key同数组的中间 mid项元素比较,
12
34
56
78
111
222
333
444
555
666
777
888
Low
high
mid
444
Key
4.排序选择、冒泡、插入法排序等。
前两种排序欲排序的数据全部输入后,再进行排序;
插入法排序每输入一项,马上插入到数组应在的位置,数组始终有序。
例 6.17 实现的步骤:
(1)输入欲排序的数据项 x;在数组 a中找 x应所处的位置 j;
(2)从数组的最后一个元素开始到下标 j依次往后移,使 j位置空出;
(3)将 x放入位置 j处,一个数据插入完成;
(4)有若干个数重复 (1)~ (3)。
)('
)(
1
i
i
ii xf
xfxx
5,例 6.18 高次方程求根有 牛顿迭代法、二分法、弦截法等
( 1)牛顿迭代法迭代公式:
思想:
对方程给定一个初值 x0作为方程的近似根,
利用迭代公式,求得 x1,当 x1为求得的近似根,
否则 x1作为 x0再迭代。
01 xx
x0 x
1
)( xf
(2) 二分法求根思想,已知求根区间 [a,b]有一根,每次把求根区间缩小一半,直到找到解或求根区间足够小。
方法,求 [a,b]的中点 c,判断
f(c)=0,c为求得的根,结束 ;
f(a)与 f(c)同号,则 [a,c]无根,代替 a;
否则 [c,b]无根,c代替 b;
使求根区间缩小一半,重复上述步骤,直到区间小于精度。
x b
a
a
c
f ( x )
Public Function halfRoot(ByVal a!,ByVal b!)
Dim c!
Do While Abs(b - a) > 0.00001
c = (a + b) / 2
If f(c) = 0 Then
Exit Do
ElseIf f(a) * f(c) > 0 Then
a = c
Else
b = c
End If
Loop
halfRoot = c
End Function
6.例 6.19数值积分有矩形法、梯形法、抛物线法 (又称辛卜生法 )等。
梯形法积分的思想 是:
将积分区间 [a,b]n等分,小区间的长度为,
第 i块小矩形的近似面积为:
整个积分的结果为这 n块小面积的累加,即,
n
abh
hxfxfs iii2 )()( 1
hxfxfS
n
i
ii
1
1
2
)()(
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
hxfbfaf
n
i
i

1
1
)())()((21
7.字符串处理例 6.20编写一个英文打字训练的程序,要求如下:
(1)在标签框内随机产生 30个字母的范文;
(2)当焦点进入文本框时开始计时,并显示当时时间;
(3)在键入文本框按产生的范文输入相应的字母;
(4)当键入满了 30个字母结束计时,禁止向文本框输入内容,键入的字符逐一范文比较,显示打字的速度和正确率。
6.7重点和难点
1,确定自定义的过程是子过程还是函数过程函数过程名有值,子过程名无值。
过程有一个返回值,则使用函数过程;
若返回多个值或无返回值,一般使用子过程 。
2.过程中形参的个数和传递方式的确定过程中参数的作用是实现过程与调用者的数据通信。
(1)从主调程序获得初值,值传递。
(2)将结果返回给主调程序,地址传递。
3,实参与形参结合时对应问题个数、类型、位置、次序一一对应。
形参是值传递,对应实参可以是表达式、常量、数组元素。
形参是地址传递,对应实参只能是简单变量。
数组、记录类型、对象只能是地址传递。
4,变量的作用域问题局部变量、静态变量、全局变量特点、作用
5,递归调用出现,栈溢出,
递归过程中有 终止的条件 和 终止时的值或某种操作;
每递归调用一次,其中的参数要向 终止方向收敛 。