Go語言簡單的統計函數

2020-07-16 10:05:07
本節中我們定義了一個聚合型別的結構體,包含使用者輸入的資料以及我們準備計算的兩種統計:

type statistics struct {
    numbers  []float64
    mean     float64
    mdian    float64
}

Go語言裡的結構體類似於C語言裡的結構體或者 Java 裡只有 public 資料成員的類(不能有方法),但是不同於 C++ 的結構體,因為它並不是一個類。Go語言裡的結構體對聚合和嵌入的支援是非常完美的。

func getStats(numbers []float64) (stats statistics) {
    stats.numbers = numbers
    sort.Float64s(stats.numbers)
    stats.mean = sum(numbers) / float64(len(numbers))
    stats.median = median(numbers)
    return stats
}

getStats 函數的作用就是對傳入的 []float64 切片(這些資料都在 processRequest() 裡得到)進行統計,然後將相應的結果儲存到 stats 結果變數中。

其中計算中位數使用了 sort 包裡的 Float64s() 函數對原陣列進行升序排列(原地排序),也就是說 getStats() 函數修改了它的引數,這種情況在傳切片、參照或者函數指標到函數時是很常見的。

如果需要保留原始切片,可以使用Go語言內建的 copy() 函數將它賦值到一個臨時變數,使用臨時變數來工作。

結構體中的 mean(通常也叫平均數)是對一連串的數進行求和然後除以總個數得到的結果。這裡我們使用一個輔助函數 sum() 求和,使用內建的 len() 取得切片的大小(總個數)並將其強制轉換成 float64 型別的變數(因為 sum() 函數返回一個 float64 的值)。

這樣我們也就確保了這是一個浮點除法運算,避免了使用整數型別可能帶來的精度損失問題。median 是用來儲存中位數的,我們使用 median() 函數來單獨計算它。

我們沒有檢查除數為 0 的情況,因為在我們的程式邏輯裡,getStats() 函數只有在至少有 1 個資料的時候才會被呼叫,否則程式會退出並產生一個執行時異常 (runtime panic)。

對於一個關鍵性應用當發生一個異常時程式是不應該被結束的,我們可以使用 recover() 來捕獲這個異常,將程式恢復到一個正常的狀態,讓程式繼續執行。

func sum(numbers []float64) (total float64) {
    for _, x := range numbers {
        total += x
    }
    return total
}

這個函數使用一個 for...range 迴圈遍歷一個切片並將所有的資料相加計算出它們的和。Go語言總是將所有變數初始化為 0,包括已經命名了的返回變數,例如 total,這是一個相當有益的設計。

func median(numbers []float64) float64 {
    middle := len(numbers) / 2
    result := numbers[middle]
    if len(numbers)%2 == 0 {
        result = (result + numbers[middle-1]) / 2
    }
    return result
}

這個函數必須傳入一個已經排序好了的切片,它一開始將切片裡最中間的那個數儲存到 result 變數中,但是如果總個數是偶數,就會產生兩個中間數,我們取這兩個中間數的平均值作為中位數返回。