240 likes | 359 Views
第 6 章 过程与函数. 6.1 子过程的定义与调用 6.2 函数的定义与调用 6.3 子过程与函数的参数传递 6.4 变量、过程的作用域 6.5 递归 6.6 常用算法举例. VB 中的过程有两种 : (1) 系统提供的内部函数过程和事件过程。 (2) 用户根据应用的需要而设计的过程。 在 VB 中根据应用的要求可分为几种类型的自定义过程 : (1) 以 “ Sub ” 保留字开始的子过程。 (2) 以 “ Function ” 保留字开始的函数过程。 (3) 以 “ Property ” 保留字开始的属性过程。
E N D
第6章 过程与函数 6.1 子过程的定义与调用 6.2 函数的定义与调用 6.3 子过程与函数的参数传递 6.4 变量、过程的作用域 6.5 递归 6.6 常用算法举例
VB中的过程有两种: • (1) 系统提供的内部函数过程和事件过程。 • (2) 用户根据应用的需要而设计的过程。 • 在VB中根据应用的要求可分为几种类型的自定义过程: • (1) 以“Sub”保留字开始的子过程。 • (2) 以“Function”保留字开始的函数过程。 • (3) 以“Property”保留字开始的属性过程。 • (4) 以“Event”保留字开始的事件过程。 • 本书仅讨论以“Sub”开头的子过程和以“Function”开头的函数过程。
6.1.1 子过程的定义 在VB中用Sub 语句定义的过程叫做子过程。可按下列格式定义子过程: Sub 子过程名 ( [ 形式参数列表 ]) 语句序列 End Sub 子过程中可以使用Exit Sub语句跳出子过程。形式参数列表由若干个形式参数构成,参数之间用逗号分隔。每个形式参数按下列格式定义:: 形式参数名 [As 类型名] 省略类型说明则表示是Variant类型。例如下列代码定义了一个子过程sub1,有两个长整型形式参数a和b,功能是将a+b的值赋给变量c 。 Sub sub1 (a as long, b as long) c = a+b End Sub
6.1.2 子过程的调用 子过程只能作为语句被调用,不能作为表达式被调用。可以用下列两种方法调用子过程: Call子过程名( [ 实际参数列表 ] ) 或 子过程名 [ 实际参数列表 ] 以上两种调用方法功能完全一致。注意,当使用 Call 语法时,参数必须在括号内。若省略 Call 关键字,则也必须省略参数两边的括号。 例如下列两条语句都可以调用了前面定义的子过程sub1,调用的结果是变量c的值变成了5。 Call sub1(2,3) 或 sub1 2,3 事件过程的调用:当有事件发生时,系统会自动调用与事件相对应的事件过程。事件过程是与对象相关联的,是对象对事件做出的响应。事件过程的名称由对象名、下划线、事件名连接而成。
6.2.1 函数的定义 在VB中,用Function语句定义的过程叫做函数。可按下列格式定义函数: Function 函数名( [ 形式参数列表 ] )[ As 类型 ] 语句序列 End Function 函数的定义与子过程类似,不同之处在于函数有返回值。函数的返回值用函数名表示,在函数返回前至少需要对函数名赋值一次。使用语句Exit Function 可以立即跳出函数。 例如下列代码定义了一个函数Fun1,有两个长整型形式参数a和b,函数的返回值为长整型,值等于a+b的结果。 Function fun1(a As Long, b As Long) As Long fun1 = a + b End Function
6.2.2 函数的调用 函数可以作为表达式来调用。格式为: 函数名[(实际参数列表)] 如果函数要直接作为语句来使用,则必须按照子过程的方式来调用,当然此时也就不能利用函数的返回值了。 例如下列三条语句都可以调用前面定义的函数Fun1: Print fun1(2, 3) Call fun1(2, 3) fun1 2, 3
6.3 子过程与函数的参数传递 1.参数传递方式 参数有两种传递方式:按值传递和按地址传递。 按值传递参数:在形式参数前加上关键字ByVal表示指定按值传递参数。调用时实际参数仅将值传递给形式参数,形式参数的任何改变不会影响实际参数。 按地址传递参数:在形式参数前加上关键字ByRef表示指定按地址传递参数。调用时如果实际参数是表达式,则传递方式与按值传递效果相同;如果实际参数是变量,则将实际参数的内存地址传递给形式参数,形式参数与实际参数指向同一个内存地址,形式参数的改变也就是实际参数的改变。
6.3 子过程与函数的参数传递 【例6-4】下列程序运行后,每次单击窗体就会显示“a=3”,“b=8”。 Option Explicit Dim a As Long Dim b As Integer Sub s(c As Long) c = c + 1 End Sub Sub Form_Click() a = 2 b = 8 s a Print "a="; a s (b) Print "b="; b End Sub 说明:本例中,圆括号用于将实际参数由变量b转换为表达式,即按值传递(s(b)),否则就是按址传递(s a)。
6.3 子过程与函数的参数传递 2.可选参数 在形式参数前加上关键字Optional可以指定该形式参数为可选参数。可选参数在过程调用时可以不提供与可选参数对应的实际参数。可选参数可以指定默认的缺省值。 3.命名参数 实际参数使用“形式参数名:= 实际参数”的格式即为命名参数。命名参数出现的位置可以与定义时的顺序无关。但需注意非命名参数仍需按定义时的位置出现,所以非命名参数在定义时应排在第一个命名参数之前。 4.数组作为参数和返回值 数组可以作为实际参数传递到过程中。此时形式参数应为Variant类型或与实际参数类型完全相同的数组类型。作为形式参数的数组应为可变数组,用数组名跟空的圆括号表示。函数的返回值也可以是数组,此时类型说明为Variant类型或“As 类型()”。
6.3 子过程与函数的参数传递 【例6-7】下列程序运行后,单击窗体时显示“9”、 “8”、“7”。 Option Explicit Function f(a() As Long) As Long() f = a End Function Sub Form_Click() Dim b() As Long Dim c(2) As Long c(0) = 9 c(1) = 8 c(2) = 7 b = f(c) print b(0) b()=f(c()) print b(1) Print f(c)(2) ’函数表达式直接带数组下标代表数组元素 End Sub
6.3 子过程与函数的参数传递 5.可变个数参数 将最后一个形式参数声明为Variant类型的数组,同时在前面加上ParamArray关键字,就变成可变个数的参数;调用过程时可输入任意个数的参数,输入的参数转变为数组的元素;也可以省略该参数。ParamArray 关键字不能与 ByVal或ByRef一起使用,也不能在参数列表中出现Optional。 【例6-8】下列程序运行后,每次单击窗体就会显示“6”。 Option Explicit Function f(a, ParamArray b()) f = a + b(0) + b(1) End Function Sub Form_Click() Print f(1, 2, 3) End Sub 注意:在用可变个数参数时,实参个数不能少于使用的形参个数。
6.4 变量、过程的作用域 • 1.过程的作用域 • 定义过程时在过程名前加关键字Private,则该过程为私有过程,仅在定义该过程的模块内有效,其他模块的过程不能调用该过程。如果没有加关键字Private或加了关键字Public,则该过程为公共过程,在整个工程内有效,其他模块内的过程可以调用该过程。 • 调用当前过程所在模块定义的过程可以直接使用过程名。调用其他窗体模块或其他标准模块定义的过程,使用“模块名.过程名”的格式。
6.4 变量、过程的作用域 • 2.变量的作用域 • 局部变量:过程的形式参数、过程中定义的变量、未经定义直接使用的变量,仅在过程中有效。 • 全局变量:在所有过程之外定义的变量,在定义变量的模块中有效。 • 公共变量:用Public定义的变量,在整个工程内有效。 • 私有变量:用Private或Dim定义的变量,仅在定义它的模块内有效。。 • 调用当前过程所在模块定义的过程可以直接使用过程名。调用其他窗体模块或其他标准模块定义的过程,使用“模块名.过程名”的格式。
6.4 变量、过程的作用域 • 3.静态变量 • 过程中可以用Static语句定义静态变量。Static语句的格式为: • Static 变量名[([数组维数])] [As [New] 类型] [, …] • Static语句与Dim语句用法相同,但Static语句不能用于定义全局变量。用Dim定义的变量在每次过程被调用时都会被分配新的存储空间,在过程调用结束时释放存储空间;静态变量只分配一次存储空间,在整个应用程序运行时,其值一直存在。 • 定义过程时在过程名前加关键字Static,则该过程为静态过程。静态过程中定义的所有变量都是静态变量,但形式参数不会成为静态变量。
6.4 变量、过程的作用域 • 【例6-11】请仔细分析下列程序: • Option Explicit • Dim a • Sub b() • Dim c • Static d • a = a + 1 • Print "模块级变量a="; a • c = c + 1 • Print "局部变量c="; c • d = d + 1 • Print "静态变量d="; d • End Sub Static Sub e(f) Dim g a = a + 1 Print "模块级变量a="; a f = f + 1 Print "形式参数f="; f g = g + 1 Print "局部变量g="; g End Sub
6.4 变量、过程的作用域 运行结果如图所示。 • Private Sub Form_Click() • Print "第1次调用过程b" • b • Print • Print "第2次调用过程b" • b • Print • Print "第1次调用静态过程e" • e 0 • Print • Print "第2次调用静态过程e" • e 0 • End Sub
6.5 递归 • 过程如果直接或间接地调用了自身,这就是递归过程。使用递归算法在某些情况下可以大大简化程序设计的复杂度,但递归容易消耗系统资源,递归层次过多会引起堆栈上溢。递归过程应有适当的机会跳出递归,否则会因递归死循环引起堆栈上溢。 • 例【6-12】求n!的递归函数。 • Public Function FN(n As Integer) As Integer • if n=1 Then • FN=1 • Else • FN=n*FN(n-1) • End if • End Function • 从以上函数可见递归是一个不断自己调用自己的过程,直到n=1为止,然后是一个不断返回到不同调用级别的过程。这过程也是一个先一级级压堆栈,后又一级级弹堆栈的过程。
6.5 递归 • 【例6-13】一只猴子一天从山上摘来一袋桃子,从这天开始,它每天都要把袋中的桃子平分为二堆,吃掉其中的一堆,然后再从剩下的桃中拿出一个解谗,等到第10天,它发现袋中只有一只桃可吃,问猴子总共摘了多少桃。 • 程序如下(运行结果如下图): • Private Sub Command1_Click() • Dim Num As Integer • Num = Val(InputBox("请输入天数:")) • Label1.Caption = "桃子数量为:" & Eat(Num) • End Sub • Private Function Eat(N As Integer) '递归函数 • If N = 1 Then • Eat = 1 • Else • Eat = 2 * Eat(N - 1) + 2 • End If • End Function
6.6 常用算法举例 • 程序设计不仅仅需要具备程序设计语言的语法知识,更重要的是要掌握基本的算法并能灵活的加以应用。下面给出一些常用算法的应用实例 。 • 1.欧几里德算法求最大公约数和最小公倍数 • 欧几里德算法也叫做辗转相除法,是计算两个数最大公约数的传统算法。步骤如下: • (1)如果a < b,则交换a和b。 • (2)令c是a / b的余数。 • (3)如果c=0,则令gcd=b并终止;否则令a=b,b=c并转向步骤(2)。 • 具体算法应用见下例:
6.6 常用算法举例 • 【例6-15】求最大公约数的函数为: • Function gcd(ByVal a As Long, ByVal b As Long) As Long • Dim c As Long • If a < b Then • c = a: a = b: b = c • End If • Do • c = a Mod b • If c = 0 Then Exit Do • a = b: b = c • Loop • gcd = b • End Function • gcd函数的调用和系统函数的调用一样,如:Print gcd(12,28)为输出12与28的最小分倍数。
6.6 常用算法举例 • 2.异或操作实现加密和解密 • 【例6-17】异或操作可以实现简单的数据加密和解密,加密解密使用相同的密码和程序。 Function code(src() As Byte, key() As Byte) As Byte() Dim a() As Byte Dim i As Long Dim j As Long Dim K As Long Dim L As Long K = UBound(src) L = UBound(key) ReDim a(k) j = 0 For i = 0 To K a(i) = src(i) Xor key(j) j = j + 1 If j > L Then j = 0 Next i code = a End Function Private Sub Form_click() Dim a() As Byte Dim b() As Byte Dim c() As Byte a = "用于加密的密码" b = "密码" Print "加密前的明文:"; a a = code(a, b) '将原文与密码进行异或运算,实现对原文的加密 Print "加密后的密文:", a a = code(a, b) '将密文与密码进行异或运算,实现对密文的解密 Print "解密后的明文:", a End Sub
6.6 常用算法举例 • 3.数制转换 • 【例6-18】将任意进制正整数形式的字符串转换为数值(十进制),实现函数如下: • Function str2n(ByVal a As String, Optional ByVal b As Long) As Long • Dim i As Long • Dim num As Long • Dim c As String * 1 • For i = 1 To Len(a) • c = mid(a, i, 1) • c = UCase(c) • If c >= "0" And c <= "9" Then • num = num * b + Val(c) 'b为b进制的基 • ElseIf c >= "A" And c <= "Z" Then • num = num * b + Asc(c) - Asc("A") + 10 • End If • Next i • str2n = num • End Function
6.6 常用算法举例 • 4.多项式求值和求导数 • 【例6-19】多项式 a0 xn + a1 xn-1+ … + an-1x+ an的值可以用下面的函数计算: • Function polynomial(a() As Double, ByVal x As Double) As Double • Dim i As Long • Dim fx As Double • fx = a(0) • For i = 1 To UBound(a) • fx = x * fx + a(i) • Next i • polynomial = fx • End Function
本章小结 • 本章介绍了与过程的定义与调用有关的内容。过程有两种基本的形式:子过程与函数,这两种形式没有本质的区别。子过程是事件响应过程采用的形式,函数则使用起来更为灵活。过程调用时的参数有两种传递方式:值传递和引用传递。值传递仅仅是复制实际参数的副本传递给形式参数,所以形式参数的变化不会影响实际参数的值,引用传递时形式参数与实际参数是同一个变量,形式参数的改变也就是实际参数的改变。过程和变量都有作用域限制,同名过程或变量在调用时有优先级的差异,变量还有生存期的问题。本章最后给出了一些实用的自定义过程,读者可从中学习算法,加深对语法知识的理解。