一个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=.
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容