一个Go语言程序示例

本文档介绍来自《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=.

你可能感兴趣的:(一个Go语言程序示例)