本文档介绍来自《Go语言编程》的简单Go语言程序示例。
程序结构
本程序是一个排序算法的实现,程序结构如下所示
sorter
|--algorithm
|--qsort
|--qsort.go
|--qsort_test.go
|--bubblesort
|--bubblesort.go
|--bubblesort_test.go
创建好目录,初始化为一个Go Module项目。
go module init sorter
编写程序
实现排序算法
冒泡算法 bubblesort.go 的实现。
package bubblesort
func BubbleSort(values []int) {
flag := true
for i := 0; i < len(values) - 1; i++ {
flag = true
for j := 0; j < len(values) - i - 1; j++ {
if values[j] > values[j + 1] {
values[j], values[j + 1] = values[j + 1], values[j]
flag = false
}
}
if flag == true {
break
}
}
}
快速排序 qsort.go 的实现。
package qsort
func quickSort(values []int, left, right int) {
temp := values[left]
p := left
i, j := left, right
for i <= j {
for j >= p && values[j] >= temp {
j--
}
if j >= p {
values[p] = values[j]
p = j
}
if values[i] <= temp && i <= p {
i++
}
if i <= p {
values[p] = values[i]
p = i
}
}
values[p] = temp
if p - left > 1 {
quickSort(values, left, p - 1)
}
if right - p > 1 {
quickSort(values, p + 1, right)
}
}
func QuickSort(values []int) {
quickSort(values, 0, len(values) - 1)
}
实现主程序
主程序用来解析命令行输入并读取输入数据,进行排序后将结果输出到对应的文件中。
主程序如下 sorter.go 如下。
package main
import (
"flag"
"fmt"
"bufio"
"io"
"os"
"strconv"
"time"
"sorter/algorithm/bubblesort"
"sorter/algorithm/qsort"
)
// 解析的命令
var infile *string = flag.String("i", "infile", "File contains values for sorting")
var outfile *string = flag.String("o", "outfile", "File to receive sorted values")
var algorithm *string = flag.String("a", "qsort", "Sort algorithm")
func main() {
flag.Parse()
if infile != nil {
fmt.Println("infile =", *infile, "outfile =", *outfile, "algorithm =", *algorithm)
}
values, err := readValues(*infile)
if err == nil {
t1 := time.Now()
switch *algorithm {
case "qsort":
qsort.QuickSort(values)
case "bubblesort":
bubblesort.BubbleSort(values)
default:
fmt.Println("Sorting algorithm", *algorithm, "is either unknown or unsupported.")
}
t2 := time.Now()
fmt.Println("The sorting process costs ", t2.Sub(t1), " to complete.")
writeValues(values, *outfile)
} else {
fmt.Println(err)
}
}
// 读取输入文件
func readValues(infile string) (values []int, err error) {
file, err := os.Open(infile)
if err != nil {
fmt.Println("Failed to open the input file ", infile)
return
}
defer file.Close()
br := bufio.NewReader(file)
values = make([]int, 0)
for {
line, isPrefix, err1 := br.ReadLine()
if err1 != nil {
if err1 != io.EOF {
err = err1
}
break
}
if isPrefix {
fmt.Println("A too long line, seems unexpected.")
return
}
str := string(line)
value, err1 := strconv.Atoi(str)
if err != nil {
err = err1
return
}
values = append(values, value)
}
return
}
// 写到输出文件
func writeValues(values []int, outfile string) error {
file, err := os.Create(outfile)
if err != nil {
fmt.Println("Failed to create the output file", outfile)
return err
}
defer file.Close()
for _, value := range values {
str := strconv.Itoa(value)
file.WriteString(str + "\n")
}
return nil
}
编写测试程序
在 Go 项目包目录内,所有以 _test.go 为后缀名的源代码文件都是 go test 测试的一部分,不会被 go build 编译到最终的可执行文件中。
测试文件中的测试函数包含以下3种:
类型 | 格式 | 作用 |
---|---|---|
单元测试 | 函数名前缀为Test | 测试程序的一些逻辑行为是否正确 |
基准测试 | 函数名前缀为Benchmark | 测试函数的性能 |
方法示例 | 函数名前缀为Example | 为文档提供示例文档 |
go test 命令会遍历所有的 *_test.go 文件中符合上述命名规则的函数,然后生成一个临时的 main 包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。
单元测试
单元测试函数名称格式是:以Test开头,跟上非小写字母开头的字符串。每个测试函数都接受一个 *testing.T 类型参数,用于输出信息或中断测试。
其中的测试方法有:
- Fail: 标记失败,但继续执行当前测试函数
- FailNow: 失败,立即终止当前测试函数执行
- Log: 输出错误信息
- Error: Fail + Log
- Fatal: FailNow + Log
- Skip: 跳过当前函数,通常用于未完成的测试用例
在 bubblesort_test.go 中添加单元测试代码。
package bubblesort
import "testing"
func TestBubbleSort1(t *testing.T) {
values := []int{5, 4, 3, 2, 1}
BubbleSort(values)
if values[0] != 1 || values[1] != 2 || values[2] != 3 ||
values[3] != 4 || values[4] != 5 {
t.Error("BubbleSort() failed. Got ", values, "Expected 1 2 3 4 5")
}
}
func TestBubbleSort2(t *testing.T) {
values := []int{5, 5, 3, 2, 1}
BubbleSort(values)
if values[0] != 1 || values[1] != 2 || values[2] != 3 ||
values[3] != 5 || values[4] != 5 {
t.Error("BubbleSort() failed. Got ", values, "Expected 1 2 3 5 5")
}
}
基准测试
基准测试函数以Benchmark 开头,参数类型是 *testing.B ,可与 Test 函数放在同个文件中。默认情况下,go test 不执行 Benchmark 测试,必须用-bench 指定基准测试函数。
B类型有以下参数:
- benchmem:输出内存分配统计
- benchtime:指定测试时间
- cpu:指定GOMAXPROCS
- timeout:超时限制
同样在 bubblesort_test.go 中添加基准测试函数。
func BenchmarkBubbleSort(b *testing.B) {
values := []int{5, 4, 3, 2, 1, 4, 4, 6, 7}
b.ResetTimer()
for i := 0; i < b.N; i++ {
BubbleSort(values)
}
}
构建与执行
构建项目
go build
构建成功后会出现 sorter.exe 。在一个文件中输入几个数字(每个数字一行)作为输入,在命令行上运行程序。
sorter -i 123.txt -o sorted.txt -a bubblesort
如果想要运行所有单元测试,可以运行
go test ./...
想要运行基准测试,可以使用如下命令。
go test ./... -bench=.