天天看點

VBA子產品執行個體:統計函數的制作和調用(基于Access)

在VB程式設計裡,子產品和子過程是非常重要的概念,把他倆用好了可以讓VB\VBA代碼變得井井有條、易于管理、易于複制。

本教程用VBA教大家做一個統計函數子產品,其中包含9種統計計算:合計值、平均值、最大值、最小值、衆數、中位數、方差、标準差、離散系數。做完子產品之後,在Access資料庫窗體中進行調用的完整過程,也會進行講解。

通過本課的學習,老鐵們可以零距離領略VB子產品化開發的優點,了解面向對象程式設計的好處。

本課分為3個部分:

  1. 子產品和子過程的概念
  2. 完整代碼分享
  3. 部分代碼解釋
VBA子產品執行個體:統計函數的制作和調用(基于Access)

學完本課做出的效果示範

— 1 —子產品和子過程的概念

子產品裡一般放一些比較常用的、有自變量因變量的函數,函數就是我們中學學的y=f(x)之類的方程,可以導出導入友善使用,并像積木一樣可以拆下來用在别的程式裡,是以取名叫子產品。

VBA子產品執行個體:統計函數的制作和調用(基于Access)

方程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個數進行以上的統計操作。

VBA子產品執行個體:統計函數的制作和調用(基于Access)

本案例先生成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個數”,這種調用的優點非常明顯:

  1. 代碼比較有層次感,不會擠在一起,防止誤操作。
  2. 可以重複調用,避免重複輸入,防止誤操作。

比如本案例,有兩個按鈕,第二個按鈕生成的是可能重複的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用函數程式設計的難度不高,但是代碼的長度超出了我的想象,看來以後講解還是以個案為主比較好。