在VB编程里,模块和子过程是非常重要的概念,把他俩用好了可以让VB\VBA代码变得井井有条、易于管理、易于复制。
本教程用VBA教大家做一个统计函数模块,其中包含9种统计计算:合计值、平均值、最大值、最小值、众数、中位数、方差、标准差、离散系数。做完模块之后,在Access数据库窗体中进行调用的完整过程,也会进行讲解。
通过本课的学习,老铁们可以零距离领略VB模块化开发的优点,理解面向对象编程的好处。
本课分为3个部分:
- 模块和子过程的概念
- 完整代码分享
- 部分代码解释
学完本课做出的效果演示
— 1 —模块和子过程的概念
模块里一般放一些比较常用的、有自变量因变量的函数,函数就是我们中学学的y=f(x)之类的方程,可以导出导入方便使用,并像积木一样可以拆下来用在别的程序里,因此取名叫模块。
方程Function一般放在模块里
子过程就是从一段很长的代码中,把可能重复的部分,单独搞出来做一个部分,需要的时候再调用一下,能有效缩短代码行数,便于管理、便于修改、便于展示。
子过程和模块的界限在VBA里不是很严格,把子过程放在模块也是可以的,把函数方程放在过程里、不放模块里也一点问题没有。
当然不应用模块和子过程,对于VB编程来说也没什么问题。对于初学者来说(比如我),理解模块和函数确实要花一些时间,一开始上手编程,可能也不容易体会模块和子过程的好处。我是花了很久之后,才知道为什么要有模块和子过程这种东西。
这种不应用子过程,也不搞模块,一条道搞到底的编程,我如果没有理解错的话,学名应该叫做面向过程编程((Procedure Oriented Programming)。
面向过程编程本身没有什么问题。但如果能熟练应用模块和子过程,可以让VB使用者编程过程更加清楚、层次分明,便于修改,对此我深有体会。本文的案例,也会非常直观的告诉读者应用模块和子过程的好处。如果我没理解错的话,这种大量应用子过程和模块的编程,应该叫面向对象编程((Object Oriented Programming)。(如果我理解的不对,还请各位读者指正)
— 2 —完整代码分享
9个常用的统计函数代码(封装在模块里)
求平均值、最大值、最小值、求和的代码,比较简单。
Function Average(a() As Integer) '求平均值
For i = 0 To Ubound(a)
s = s a(i)
c = c 1
Next i
Average = s / c
End Function
Function sum(a() As Integer) '求和
For i = 0 To UBound(a)
sum = sum a(i)
Next i
End Function
Function Max(a() As Integer) '最大值
Max = a(0)
For i = 0 To UBound(a)
If a(i) > Max Then Max = a(i)
Next i
End Function
Function Min(a() As Integer) '最小值
Min = a(0)
For i = 0 To UBound(a)
If a(i) < Min Then Min = a(i)
Next i
End Function
求中位数、方差、标准差、离散系数的代码,相对比较简单。
Function Median(a() As Integer, n As Integer) '中位数
If n Mod 2 = 1 Then
Median = a(n / 2)
Else
Median = (a(n / 2) a(n / 2 1)) / 2
End If
End Function
Function Variance(a() As Integer) '方差
For i = 0 To UBound(a)
s = s (a(i) - Average(a)) ^ 2
Next
Variance = s / (UBound(a) 1)
End Function
Function StandardDeviation(a() As Integer) '标准差
For i = 0 To UBound(a)
s = s (a(i) - Average(a)) ^ 2
Next
StandardDeviation = (s / (UBound(a) 1)) ^ (1 / 2)
End Function
Function DiscreteCoefficient(a() As Integer) '离散系数
For i = 0 To UBound(a)
s = s (a(i) - Average(a)) ^ 2
Next
DiscreteCoefficient = ((s / (UBound(a) 1)) ^ (1 / 2)) / Average(a)
End Function
最难的就是众数的代码,因为众数不一定只有一个,编程非常复杂,具体原理本课程暂不介绍。
Function Mode(a() As Integer) '众数
Dim b, c(), f(), i, j, k, x()
k = UBound(a)
ReDim c(k), f(k)
For i = 0 To k - 1
If f(i) = 0 Then
c(i) = 1
For j = i 1 To k
If a(j) = a(i) Then
c(i) = c(i) 1
f(j) = 1
End If
Next
End If
Next
If f(i) = 0 Then c(i) = 1
b = 1
For i = 0 To k
If c(i) > b Then b = c(i)
Next
'若所有数据都是众数,则没有众数
For i = 0 To k
If c(i) <> b And c(i) <> 0 Then Exit For
Next
If i = k 1 Then
ReDim x(0)
x(0) = "没有众数"
Mode = x
Exit Function
End If
'找出所有众数
j = 0
For i = 0 To k
If c(i) = b Then
ReDim Preserve x(j)
x(j) = a(i)
j = j 1
End If
Next
Mode = x
End Function
调用代码
本案例利用Access数据库的窗体功能,先生成了25个随机数并进行排序,然后再对这25个数进行以上的统计操作。
本案例先生成25个数,再对其进行统计操作
生成不重复数的子过程如下:
Public Sub NoRepeatedNumbers(a) '生成25个不重复的数
For i = 0 To 24
a(i) = Int(30 * Rnd 1)
For j = 0 To i - 1 '这段是防止重复的代码
If a(i) = a(j) Then '如果重复了再次选择
i = i - 1
End If
Next j
Next i
End Sub
子过程跟函数不一样,没有返回值,而函数有x也必须有个结果y。
生成重复数的子过程如下:
Public Sub RepeatedNumbers(a) ''生成25个可能有重复的数
For i = 0 To 24
a(i) = Int(30 * Rnd 1)
Next i
End Sub
排序利用的是冒泡算法:
Public Sub Bubble(a) '冒泡算法
Dim i, j As Integer
For i = 0 To 24
For j = i 1 To 24
If a(i) > a(j) Then
t = a(i) 't作为中间变量,冒泡算法常见
a(i) = a(j)
a(j) = t
End If
Next j
Next i
End Sub
Access窗体利用按钮控件,进行自动的计算,并将计算结果输出至从Text1到Text10共10个文本框中,其中Text1是生成的25个随机数(已经排好序),Text2-Text10是统计函数的直接调用,非常简便,其中数组a是变量,输出结果直接显示在Text1中。
Option Compare Database
Dim a(24) As Integer '数组一共25个数字
Dim i As Integer
Dim j As Integer
Dim tempStr As String '中间变量,用于数列分行
Dim t As Double '中间变量,排序用
Private Sub Command1_Click()
Text1 = ""
tempStr = " "
Call NoRepeatedNumbers(a)
'下面这段是生成不重复的25个数字,从1-30的数字里选择
Call Bubble(a) '这段是排序,运用冒泡算法
'这段是在文本框里生成25个数字
For i = 0 To 24
Text1 = Text1 tempStr CStr(a(i)) 'CStr转换成字符串
If (i 1) Mod 5 = 0 Then
tempStr = Chr(13) Chr(10) " " '换行
Else
tempStr = " "
End If
Next i
'进行统计计算
Text2 = Average(a)
Text3 = sum(a)
Text4 = Max(a)
Text5 = Min(a)
Text6 = Median(a, 25)
Text7 = Mode(a)
Text8 = Variance(a)
Text9 = StandardDeviation(a)
Text10 = DiscreteCoefficient(a)
End Sub
注意两个Call,是调用子过程“冒泡算法”和“生成25个数”,这种调用的优点非常明显:
- 代码比较有层次感,不会挤在一起,防止误操作。
- 可以重复调用,避免重复输入,防止误操作。
比如本案例,有两个按钮,第二个按钮生成的是可能重复的25个数字(去掉了防止重复的代码)
然后第二个按钮代码如下,和上一段代码很类似,只改动了第二个Call召唤的子过程
Private Sub Command2_Click()
Text1 = ""
tempStr = " "
Call RepeatedNumbers(a) '是生成可能重复的25个数字,从1-30的数字里选择
Call Bubble(a) '这段是排序,运用冒泡算法
'这段是在文本框里生成25个数字
For i = 0 To 24
Text1 = Text1 tempStr CStr(a(i)) 'CStr转换成字符串
If (i 1) Mod 5 = 0 Then
tempStr = Chr(13) Chr(10) " " '换行
Else
tempStr = " "
End If
Next i
'进行统计计算
Text2 = Average(a)
Text3 = sum(a)
Text4 = Max(a)
Text5 = Min(a)
Text6 = Median(a, 25)
Text7 = Mode(a)
Text8 = Variance(a)
Text9 = StandardDeviation(a)
Text10 = DiscreteCoefficient(a)
End Sub
注意观察第6行。两个按钮的区别就是一个是“Call RepeatedNumbers(a)”,一个是“Call NoRepeatedNumbers(a)”。
— 3 —部分代码讲解
9个常用的统计函数代码(封装在模块里)
求平均值、最大值、最小值、求和的代码,比较简单。利用For循环遍历即可,
求和操作:利用for循环,遍历数组从0一直到最大下标Ubound,将数组数字层层相加。
求平均值操作:在求和的基础上除以数组总数量,每遍历一次,分母 1.
求最大最小值:基本思想是把数组中的所有数字都和Max和Min比较一遍,Max和Min值取数组第一个数。这种方法只适合排序好的数组。
求中位数、方差、标准差、离散系数的代码编写思路:基本都是对照函数方程进行编写,有的计算平均数是调用了之前的函数代码。这些代码中蕴含的数学更多一些,也比代码本身重要。
调用代码
子过程的调用是采用Call语句。
函数的调用就是直接调用函数名称。
本章先讲到这里,VBA用函数编程的难度不高,但是代码的长度超出了我的想象,看来以后讲解还是以个案为主比较好。