470 likes | 584 Views
第 6 章 过 程. 将程序分割成较小的逻辑部件就可以简化程序设计任务,称这些部件为 过程 ,它们可以变成增强和扩展 Visual Basic 的构件。 VB 中有两类过程,一类是内部过程,另 — 类是外部过程,每 — 类过程又可分为函数过程和事件过程。 内部过程是由系统提供、可直接调用。 外部过程由用户自己定义和编写。. 6.1 过程的定义. 6.2 过程的调用. 6.3 参数传送. 6.4 变量与过程的作用范围. 6.5 递归. 6.6 综合实例. 6.1 过程的定义.
E N D
第6章 过 程 • 将程序分割成较小的逻辑部件就可以简化程序设计任务,称这些部件为过程,它们可以变成增强和扩展 Visual Basic 的构件。 • VB中有两类过程,一类是内部过程,另—类是外部过程,每—类过程又可分为函数过程和事件过程。 • 内部过程是由系统提供、可直接调用。 • 外部过程由用户自己定义和编写。
6.1 过程的定义 6.2 过程的调用 6.3 参数传送 6.4 变量与过程的作用范围 6.5 递归 6.6 综合实例
6.1 过程的定义 自定义Sub子过程和Functin函数过程有以下两种方法。 1.利用“添加过程”对话框定义 图6-1 “添加过程”对话框
2.直接在代码窗口定义 • 在窗体或标准模块的代码窗口之外,输入Sub子过程名或Function函数过程即可。 • 子过程的语法格式为: • [Private | Public] [Static]Sub<过程名>([<参数列表>]) • [<语句块1>] • [Exit Sub] • [<语句块2>] • End Sub • 函数过程的语法格式为: • [Public|Private][Static]Function<函数过程名>[(<参数列表>)][As<类型>] • [<语句块1>] • [函数过程名 =<表达式>] • [Exit Function] • [<语句块2>] • [<函数过程名> =<表达式>] • End Function
例:由CSSJ过程产生一批随机整数,数据的取值范围为-10 ~ 10 之间 • Public Sub CSSJ() • Randomize • Form1.Print "原始数据" • Dim i As Integer • Dim j As Integer • For i = 1 To n • '随机产生0或1,为0时取负,为1时取正 • j = Int(Rnd * 2) • If ------ 1 ----- Then J = -1 • a(i) = j * Int(Rnd * (n + 1)) • Form1.Print a(i); • Next i • Form1.Print • End Sub
TJ过程:将数据中小于零的数及它们的积打印出来TJ过程:将数据中小于零的数及它们的积打印出来 • Public Sub TJ() • Dim i As Integer • Dim t As Single • t= --------2 -------- • For i = 1 To 10 • If -------- 3 -------- Then • t = t * a(i) • End If • Next i • Form1.Print "T="; t • End Sub
6.2 过程的调用 6.2.1 子过程的调用 1.使用Call语句调用过程 Call语句的格式为: Call 过程名(<实际参数表列>) 如:call cssj call tj 2. 将过程名作为语句使用 如果省略 Call 关键字而直接将过程名作为语句使用,也必须省略<实际参数表列> 外面的括号。 过程名 如:cssj tj
6.2.2 函数过程的调用 • 1.使用Call语句调用Function过程 • 同Sub过程一样,同样可以使用Call语句调用Function过程,这时的Function过程返回值的过程实质上相当于Sub过程,调用后,将返回所有在参数列表中列出的参数的值。 • 使用函数名过程调用Function过程 • 与Sub过程不同,不能单独将Function过程作为一个语句使用。
Private Sub Command1_Click() Dim a As Double, b As Double, c As Double, s As Double Dim intYesorNo As Integer a = Val(Text1.Text): b = Val(Text2.Text): c = Val(Text3.Text) If a + b > c And b + c > a And c + a > b Then Call triarea(s, a, b, c) Picture1.Print " 面积 ="; s Else suberr intYesorNo End If End Sub 将例6-1中两个Sub过程改为Function过程。
6.3 参数传送 • 形参 • Sub过程和Function函数过程中的<参数列表>中的参数称为形式参数. • 实参 • 在程序中调用Sub过程和Function函数过程时<参数列表>中称为实际参数。过程调用的过程,就是实参与形参结合的过程。
6.3.1 形参与实参的传送方式 6.3.2 传址与传值 6.3.3 数组参数的传递 6.3.4 可选参数与可变参数
6.3.1 形参与实参的传送方式 1. 按位置传送 按位置传送就是实参的次序与形参的次序应匹配,位置次序一一对应。 2. 指名传送 指名传送就是显式地指出与形参相结合的实参,将形参与实参用“:=”连接起来,这样,就不必要求形参与实参按位置次序一一对应。如: Call mysub(a ,b (),"王大虎" ,d ) 与 Call mysub(x:=a ,yarray:=b (),zstr:="王大虎" ,w:=d ) 以及 Call mysub(yarray:=b (),x:=a ,w:=d ,zstr:="王大虎") 是等价的。
6.3.2 传址与传值 1. 传址 在VB中,默认情况下实参与形参是传址传递数据的。 2. 传值 传值就是通过传送实参的值而不是传送地址的方法给被调用过程中的形参。被调用过程并没有访问实参的地址,因而也就不会改变实参的值。 在VB中,区分传址与传值的标志为: • 传值时:在形参前加上ByVal • 传址时:在形参前加上ByDef或者是默认。
3. 传送方式的选择 • 若要返回调用结果——传地址; • 不希望过程修改实参的值——传值。 • 形参是数组、自定义类型时,只能传地址。
事件过程: Private Sub Command1_Click() Dim i%, s(), a%, k% Cls a = Val(Text1.Text) ReDim s(1 To a) For i = 1 To a s(i) = sum(a, i) k = k + 1 Print "i="; i, "k="; k, "s" & i & "="; s(i) If k > a Then Exit For Next i End Sub
6.3.3 数组参数的传递 例6-5:求数组元素的和。 函数过程: Public Function sum(p()) Dim m%, n% For m = LBound(p, 1) To UBound(p, 1) For n = LBound(p, 2) To UBound(p, 2) sum = sum + p(m, n) Next n,m End Function 事件过程: Private Sub Command1_Click() Dim s(), a%, b%, k%, sums% Cls a = CInt(Text1.Text):b = CInt(Text2.Text) ReDim s(1 To a, 1 To b) For j = 1 To b For i = 1 To a s(i, j) = Int(80 * Rnd + 10) Next i,j sums = sum(s()):Print "sums="; sums End Sub
6.3.4 可选参数与可变参数 1. 可选参数 例6-6:求两个数的和的函数过程。 函数过程: Public Sub multisum(mysum, first As Integer, second As Long, Optional third) mysum = first + second If Not IsMissing(third) Then mysum = mysum + third End Sub 事件过程: Private Sub Command1_Click() Dim a As Integer, b As Long, c As Double a = 10: b = 20 Call multisum(Sum, a, b) Print "sum="; Sum a = 10: b = 20: c = 30 Call multisum(Sum, a, b, c) Print "sum="; Sum End Sub
2. 可变参数 在传递参数时,如果要求参数的数目可以变化,则在函数过程或Sub过程中一般采用如下语法: Sub <过程名> ([<其他参数>,]ParamArray <数组名>) 例6-7:将上述程序修改为求任意个数的和。 Private Sub Command1_Click() Dim s(), a As Integer, b As Long, c As Variant, d As Double s = Array(1, 2, 3, 4, 5) Call multisum(Sum, s(0), s(2), s(4)) Print "sum="; Sum a = 1000: b = 10000: c = 100: d = 10 Call multisum(Sum, a, b, c, d) Print "sum="; Sum End Sub Public Sub multisum(mysum, ParamArray p()) mysum = 0 For Each x In p mysum = mysum + x Next End Sub
窗体模块(.frm) 类模块 (.cls) 标准模块 (.bas) Sub子过程 事件过程sub() Sub过程 6.4 变量与过程的作用范围 VB应用程序 (.vbp文件) 函数过程 (Function) 函数过程 Function () 图6-2 VB应用程序的组成
1.窗体/模块级过程 在某个窗体或标准模块中定义的Sub过程或函数过程前加上Private关键字,则该过程只能被包含过程的窗体或标准模块中的过程调用。 6.4.1 过程的作用域 2.全局级 在某个窗体或标准模块中定义的Sub过程或函数过程前加上Pulbic关键字或缺省,则该过程为全局级过程,可以被应用程序的所有窗体或标准模块中的过程调用。其调用方式根据过程所处的位置有如下两种: 1)在窗体中定义的过程,当外部过程要调用时,应在被调用的过程名前加上所处的窗体名。 2)在标准模块中定义的过程,如果过程名唯一,则任何外部过程都可以直接调用,否则应在被调用的过程名前加上所处的标准模块名。
6.4.2变量的作用域 1.局部变量(动态变量) 局部变量是只能在一个函数或过程中访问的变量,其他过程或函数不能访问此变量的数据。 2. 窗体/模块级变量 窗体/模块级变量是指在一个窗体/模块的任何过程之外,即在“通用声明”段中用Dim或Private语句声明的变量。 3. 全局变量 全局变量是指在一个窗体/模块的任何过程之外声明的变量,即在“通用声明”段中用Public语句声明的变量。
6.4.3 静态变量 局部变量可以使用Static关键字声明为静态变量,这样,在每次调用过程时,静态变量可以保存原先的值。声明局部变量的语法为: Static <变量名> [As <类型>] Static Sub <过程名>([<参数列表>]) Static Function <函数名>([<参数列表>])[As <类型>] 在过程名前加上Static关键字,表明该过程的变量都是静态变量。
例6-7:下面程序中有三个同名的变量wv,图6-3是运行该程序后,三次单击Command1后显示的结果。例6-7:下面程序中有三个同名的变量wv,图6-3是运行该程序后,三次单击Command1后显示的结果。 Public wv As Integer Private Sub Command1_Click() Dim wv As Integer wv = wv + 3 Call wholesub Print "Command1事件过程的wv="; wv, "全局的wv="; Form1.wv End Sub Public Sub wholesub() Static wv As Integer wv = wv + 1 Form1.wv = wv + 5 Print "wholesub过程的wv="; wv, "全局的wv="; Form1.wv End Sub Private Sub Form_Load() wv = 1 End Sub 图6-3 全局变量、局部变量、静态变量
利用静态变量记录某个事件被触发的次数 例6-9: Private Sub Command1_Click() Call password End Sub Private Sub password() Static counter As Integer Dim password As String counter = counter + 1 password = InputBox("请输入密码") If password = "12345" Then counter = 0 If counter = 3 Then MsgBox "请核对密码后再运行程序", vbOKOnly + vbInformation, "密码错误" End End If End Sub
记录开关变量的值 例6-9: Private Sub Command1_Click() Static tandf As Boolean tandf = Not tandf If tandf = True Then Command1.Caption = "确认修改" Text1.SetFocus Else Command1.Caption = "修改" Text1.Text = "" End If End Sub
6.5 递归 • 在VB中可以使用递归调用。通俗地讲,递归就是一个过程调用过程本身。在递归调用中,一个过程执行的某一步可能要用到它自身上面调用的结果。 • 递归分为两种类型,一种是直接递归,即在过程中调用过程本身;—种是间接递归,即间接地调用一个过程。
例6-10:求阶乘Factorial(n)=n!的函数 Private Sub Command1_Click() Dim myn As Double Cls myn = InputBox("请输入小于171的数") If myn <= 170 And myn >=0Then Print myn & "!="; factorial(myn) End If End Sub Public Function factorial(n As Double) As Double If n > 1 Then factorial = n * factorial(n - 1) Else factorial = 1 End If End Function
例6-11:用递归过程求两个整数m和 n的最大公约数。 Private Sub Command1_Click() Dim x%, y%, gcdinxy% Cls x = CInt(InputBox("请输入第一个整数")) y = CInt(InputBox("请输入第二个整数")) Print x & "和" & y & "的最大公约数为" & greatcd(x, y) End Function Public Function greatcd(m As Integer, n As Integer) As Integer If (m Mod n = 0) Then greatcd = n Print m, n Else greatcd = greatcd(n, m Mod n) Print m, n End If End Function 图6-4 求最大公约数
6.6 综合实例 6.6.1 查找 1.顺序查找 顺序查找即从数组的第一个元素开始与关键字进行比较,若相等则查找成功,否则,将下一个元素与关键字进行,直到最后一个,如果某个元素与关键字相等,则查找成功且停止继续查找。若找不到,则查找失败。
例6-13:利用顺序查找法找出数组中的某个数。例6-13:利用顺序查找法找出数组中的某个数。 程序代码为: Dim a() Private Sub Command1_Click() Dim myrecord As Integer myword = Val(InputBox("请输入要查找的关键字")) Call search(a, myword, myrecord) If myrecord = -1 Then MsgBox "没有您要查找的关键字", vbInformation + vbOKOnly, "查询结果" Else MsgBox "您要查找的关键字位置为" & myrecord, vbInformation + vbOKOnly, "查询结果" End If End Sub Public Sub search(p(), ByVal keyword, record As Integer) Dim x As Integer record = -1 For x = LBound(p) To UBound(p) If p(x) = keyword Then record = x: Exit For End If Next End Sub Private Sub Form_Click() ReDim a(0 To 50) For i = 0 To 50 a(i) = Int(Rnd * 91 + 10) Next End Sub
例6-14:从数组a中删除数组b中已有的数后形成新的数组x。例6-14:从数组a中删除数组b中已有的数后形成新的数组x。 Dim a(), b() Private Sub Form_Click() ' 产生数组a和b ReDim a(1 To 10) : ReDim b(1 To 10) For i = 1 To 10 : a(i) = Int(Rnd * 31 + 40): Print a(i); :Next: Print For i = 1 To 10 : b(i) = Int(Rnd * 21 + 40): Print b(i); :Next : Print End Sub Private Sub Command1_Click() Dim myrecord As Integer For k = 1 To 10 For l = 1 To 10 If a(k) = b(l) Then ' 在b中找到a中的元素 Call delete(a, k,9-n) n = n + 1 ' n存放找到的元素个数 End If Next Next For i = 1 To 10 –n : Print a(i); : Next ' a中只剩下10-n个有效元素 End Sub Public Sub delete(x(), m,n) For i = m To n : x(i) = x(i + 1) : Next ' 依次前移 x(n + 1) = 0 ' 最后一个元素清零 End Sub
2. 二分法查找 顺序查找法适合数组较小的情况,当数组较大时,可以采用二分法查找以提高查找效率。 二分查找的思路是:将要查找的关键字与有序数组中中间元素相比较,如果相等,则查找成功,否则判断关键字落在数组的哪一部分,对该部分继续按照上述方法查找。
例6-15:利用二分查找法找出数组中的某个数。例6-15:利用二分查找法找出数组中的某个数。 Dim a(1 To 10) Private Sub Form_Click() ' 产生随机数组并排序 Dim mykeyword%, myrecord% For i = 1 To 10 a(i) = Int(Rnd * 91 + 10): Print a(i); Next Call sorter(a) ' 调用排序函数 End Sub Public Sub sorter(x()) ' 插入法排序函数 For i = 2 To 10 t = x(i): j = i - 1 Do While t < x(j) x(j + 1) = a(j) : j = j - 1 If j = 0 Then Exit Do Loop x(j + 1) = t Next End Sub
Public Sub dichotomy(x(), ByVal low%, ByVal high%, ByVal keyword, record) ' 二分法查询函数 Dim mid As Integer mid = (low + high) / 2: Print If x(mid) = keyword Then record = mid : Exit Sub ' 找到关键字则退出函数过程 ElseIf low > high Then record = -1: Exit Sub ' 直到 low > high还找不到关键字则退出函数过程 End If If keyword < x(mid) Then high = mid – 1 ' 取左半部 Else low = mid + 1 ' 取右半部 End If Call dichotomy(x(), low, high, keyword, record) ' 递归 End Sub Private Sub Command1_Click() ' Command1_Click事件过程 mykeyword = Val(InputBox("请输入要查找的关键字")) ' 给出关键字 Call dichotomy(a(), LBound(a), UBound(a), mykeyword, myrecord) ' 调用查询函数 If myrecord = -1 Then MsgBox "没有您要查找的关键字", vbInformation + vbOKOnly, "查询结果" Else MsgBox "您要查找的关键字位置为" & myrecord, vbInformation + vbOKOnly, "查询结果" End If End Sub
数组由单击窗体事件时随机产生并调用排序过程进行排序。Command1_Click事件过程给出关键字,调用查询函数,利用对话框显示查询结果。查询函数使用了递归调用。数组由单击窗体事件时随机产生并调用排序过程进行排序。Command1_Click事件过程给出关键字,调用查询函数,利用对话框显示查询结果。查询函数使用了递归调用。 如果不采用递归调用,可以将查询函数作如下修改: Public Sub dichotomy(x(), ByVal low%, ByVal high%, ByVal keyword, record) Dim mid As Integer record = -1: newmid = -1: mid = (low + high) / 2 Do While newmid <> mid And low <= high If x(mid) = keyword Then record = mid : Exit Do End If If keyword < x(mid) Then high = mid - 1 Else low = mid + 1 End If newmid = mid : mid = (low + high) / 2 Loop End Sub
6.6.2 加密与解密 例6-16:为输入的文本加密。 基本原理:将英文字符的ASCⅡ码加上某个值,使其变成另外一个字符,实现加密。解密的过程则是加密的逆操作。 程序代码为: Dim everytext As String ' 声明窗体级变量,everytext保存原文 Public Function encipher(origtext As String) ' 加密函数 ,形参origtext对应实参everytext Dim newtext As String, currchar As String, newchar As String Dim origlen As Integer origlen = Len(origtext) For k = 1 To origlen ' 逐个字符加密 currchar = Mid$(origtext, k, 1): newchar = Chr$(Asc(currchar) + 3) newtext = newtext & newchar Next encipher = newtext End Function
Public Function revert(origtext As String) As String ' 解密函数 Dim newtext As String, currchar As String, newchar As String,origlen As Integer origlen = Len(origtext) For k = 1 To origlen ' 逐个字符还原 currchar = Mid$(origtext, k, 1): newchar = Chr$(Asc(currchar) - 3) newtext = newtext & newchar Next revert = newtext End Function
Private Sub cmdintext_Click() ' 输入原文事件,对应图6-10中的“输入原文”按钮 • everytext = InputBox("请输入需要加密的原文", "未加密") • End Sub • Private Sub Cmdencipher_Click() ' 加密事件,对应图6-10中的“加密”按钮 • Dim enciphertext As String • enciphertext = encipher(everytext) ' 调用加密函数过程 • MsgBox enciphertext, vbExclamation, "加密成功" ' 对话框中显示加密的文本 • End Sub • Private Sub Cmdrevert_Click() ' 解密事件,对应图6-10中的“解密”按钮 • Dim reverttext As String • reverttext = revert(encipher(everytext)) • MsgBox reverttext, vbExclamation, "解密成功" ' 对话框中显示解密的文本 • End Sub
6.6.3 用梯形法求定积分 函数f(x)在[a,b]区间的积分 I = 的几何意义为求曲线f(x)与直线y=0,x=a,x=b所围成的曲顶矩形的面积(参见图6-6)。采用梯形法求定积分是原函数不容易找到时常用的方法。 设h=(b-a)/n n为曲顶矩形的数目,n越大,精度越高 则有定积分I 的近似值s: s= 可以将上式改为迭代形式 s= s=s+h×( 图6-6 梯形法求定积分示意图 例6-17:编程用梯形法求定积分 图6-7 求定积分程序运行界面
Public Function fun(ByVal x As Double) As Double fun = x * x * x + x - 2 End Function Public Function integral(ByVal a As Single, ByVal b As Single, ByVal n As Integer) Dim s As Double, h As Double h = (b - a) / n : s = 0.5 * h * (fun(a) + fun(b)) For i = 1 To n - 1 s = s + fun(a + i * h) * h Next integral = s End Function Private Sub Command1_Click() If Text1.Text <> "" And Text2.Text <> "" And Text3.Text <> "" Then a = Val(Text1.Text) : b = Val(Text2.Text):n = Val(Text3.Text) : Text4.Text = integral(a, b, n) Else MsgBox "请输入数据", vbInformation + vbOKOnly, "缺少数据" End If End Sub Private Sub Command2_Click() Text1.Text = "" : Text2.Text = "" : Text3.Text = "" : Text4.Text = "" : Text1.SetFocus End Sub
6.6.4 高次方程求根 在数学运算中,经常会遇到高次方程求根的问题。常用的方法有牛顿切线法、二分法和弦截法。 1. 牛顿切线法 牛顿法的思路是: 为方程 给定一个初值x0作为方程的近似根,则可以用迭代公式 求解方程的更精确的近似根xi+1。条件是:对任意给定的较小值ε,总可以找到|xi+1-xi|<ε。 牛顿切线法的几何意义为曲线 在(xi, )处的切线与x轴 的交点。
图6-8 牛顿切线法求方程的近似根 图6-9 牛顿切线法求方程的近似根流程图
例6:编程用牛顿切线法求解方程 =0 的近似根。 (1)界面设计 图6-10 牛顿切线法程序运行界面
Public Function fun(ByVal x As Double) As Double fun = (5 * x * x - 6 * x - 11) * x + 15 End Function Public Function deri(ByVal x As Double) As Double deri = (15 * x - 12) * x - 11 End Function Public Function itera(ByVal x0 As Double, ByVal a As Double) As Double Dim k As Double Do x = x0 - fun(x0) / deri(x0):k = Abs(x - x0):x0 = x Loop While k >= a itera = x End Function Private Sub Command1_Click() Dim x0 As Double, a As Double If Text1.Text <> "" And Text2.Text <> "" Then x0 = Val(Text1.Text) : a = Val(Text2.Text) : Text3.Text = itera(x0, a) Else MsgBox "请输入数据", vbInformation + vbOKOnly, "缺少数据" End If End Sub Private Sub Command2_Click() Text1.Text = "" : Text2.Text = "" : Text3.Text = "" : Text1.SetFocus End Sub
2. 二分法 二分法的思路是:如方程 在[a,b]区间有一个根,则 与 必然符号相反,这样,a与b的中点c必然接近 的根x。如果 或 (ε为给定的精度),则c就为所求的近似根,否则, 和 必然有一个小于0,于是,可以在新的区间[a,c]或 [c,b]求新的中点作为近似根。重复这一过程,直到满足给定的精度。 例6-19:编程用二分法求解上题方程的近似根。 (1)界面设计 图6-11 二分法求方程近似根的程序运行界面
Private Sub Command1_Click() Dim a As Double, b As Double, d As Double a = Val(Text1.Text): b = Val(Text2.Text): d = Val(Text3.Text) If Text1.Text <> "" And Text2.Text <> "" Then Text4.Text = dich(a, b, d) Else MsgBox "请输入数据", vbInformation + vbOKOnly, "缺少数据" End If End Sub Public Function dich(a As Double, b As Double, d As Double) As Double Dim c As Double c = (a + b) / 2 If Abs(fun(c)) <= d Then dich = c ElseIf fun(c) * fun(a) < 0 Then b = c: dich = dich(a, b, d) Else a = c: dich = dich(a, b, d) End If End Function The end