第一個程式HelloWorld
按照國際慣例,我們來實現helloworld
package main
import "fmt"
func main() {
fmt.Println("Hello,World")
}
package main標註程式位置
import "fmt"匯入fmt包,類似python,包名需要使用""圈起,如果引入的包太多,考慮使用import()
import (
"fmt"
"time"
"os"
)
func main定義函式名為main的函式,接著在函式里面使用fmt.Println函式輸出“hello,world”
變數和常量
在golang中,定義一個變數可以使用var語法
var name type = value
如果是在函式體內,你還可以使用更加簡潔的命名方式
varName := value
變數的命名採用駱駝法,首字母小寫,往後每個新單詞的首字母大寫。如myString。如果想要定義的內容被其他外部包使用,則首字母也要大寫
package main
import "fmt"
func main() {
const c = 45.2 //常量
var myInt int = 23
myString := "hello"
var myBool1,myBool2 bool
myBool1 = true
myBool2 = false
fmt.Println(myInt,c)
fmt.Printf("my string: %s, type: %T",myString,myString)
fmt.Printf("my bool: %v,type %T",myBool1,myBool2)
myBool1 = false //賦值語句,myBool1之前已經使用:=宣告過了,所以直接使用=
fmt.Println(myBool1)
}
if,switch和for
if語句可以如下定義:
if condition {
}
if-else:
if condition {
} else {
}
if-else if-else
if condition {
} else if condition2 {
} else {
}
在使用if-else時,必須保證else與if的}在同一行,否則else無效
package main
import "fmt"
func main() {
var first int = 10
var cond int
if first <= 0 {
fmt.Printf("first is less than or equal to 0\n")
} else if first > 0 && first < 5 {
fmt.Printf("first is between 0 and 5\n")
} else {
fmt.Printf("first is 5 or greater\n")
}
if cond = 5; cond > 10 {
//此處cond = 5為賦值語句,等同於cond=5 if cond > 10 {}
fmt.Printf("cond is greater than 10\n")
} else {
fmt.Printf("cond is not greater than 10\n")
}
}
switch語句定義如下
switch var {
case condition1:
.....
case condition2:
.....
case condition3:
fallthrough
default:
......
}
上述的var,default,fallthrough,都是非必須選項,default代表switch最後預設執行,fallthrough代表在執行case後繼續執行後續的case
package main
import "fmt"
func main() {
num := 10
switch num {
case 1:
fmt.Println("num is 1")
case 10:
fmt.Println("num is 10")
default:
fmt.Println("default")
}
}
試著修改上面的第二case為12,看看輸出結果是否不同?
當然我們在比較數值時,會把num放到switch裡面
package main
import "fmt"
func main() {
var num int = 100
switch {
case num == 1:
fmt.Println("Num is 1")
case num > 1 && num < 60:
fmt.Println("1 < Num < 60")
case num >= 60 && num < 100:
fmt.Println("60 <= num < 100")
case num == 100:
fmt.Println("Num is 100")
fallthrough
default:
fmt.Println("default")
}
}
上述程式碼加入了fallthrough,使得原本不表達的default也被執行了
記得if的賦值語句,你也可以把上面的程式碼這樣寫
package main
import "fmt"
func main() {
switch num := 100; {
case num == 1:
fmt.Println("Num is 1")
case num > 1 && num < 60:
fmt.Println("1 < Num < 60")
case num >= 60 && num < 100:
fmt.Println("60 <= num < 100")
case num == 100:
fmt.Println("Num is 100")
fallthrough
default:
fmt.Println("default")
}
}
for 語句的語法如下:
for 初始化語句;條件語句;修飾語句{};
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
fmt.Printf("i is %d\n", i)
}
}
上面是一個簡單的for語句,有趣的是,for語句中的初始化,條件,修飾都不是必須選項,你可以這樣修改
package main
import "fmt"
func main() {
i := 0
for i < 10 {
fmt.Printf("i is %d\n", i)
i++
}
}
只保留條件語句,甚至你可以連條件語句也去掉:
package main
import "fmt"
func main() {
i := 0
for {
fmt.Printf("i is %d\n", i)
if i == 10 {
break
}
i++
}
}
for{}的功能其實等同於while,go語言沒有while的語法
接下來我們來看另一個for語句
package main
import "fmt"
func main() {
str := "Hello,World"
for i := 0; i < len(str); i++ {
fmt.Printf("Character on position %d is : %c\n", i, str[i])
}
}
獲取字串或陣列的下標和對應值是比較常見的,go提供了for-range結構可以優雅的實現,go-reange語法:
for pos, char := range str {}
所以上面的程式碼可以for-range優雅的表述為
package main
import "fmt"
func main() {
str := "Hello,World"
for pos, cha := range str {
fmt.Printf("Character on position %d is : %c\n", pos, cha)
}
}
陣列,切片,map
我們可以這樣定義一個陣列:
var name [len]type //e.g. var myArr [10]int
類似於c語言,還可以有如下定義:
var name = new([len]int) //e.g. var myArr = new([10]int)
上述兩種定義方式產生的效果不同:
第一種定義方式產生的是[len]int e.g. [10]int
第二種定義方式產生的是*[len]int e.g. *[10]int
我們可以透過下面兩個程式來體會兩者的不同
package main
import "fmt"
func main() {
var arr1 [5]int
test(arr1)
fmt.Println("The orignal array is:", arr1)
}
func test(a [5]int) {
for i, v := range a {
fmt.Printf("Old value is:%d ----> ", v)
a[i] = i
fmt.Printf("New value is:%d\n", a[i])
}
fmt.Println("The array is: ", a)
}
上面的程式可以看到,當傳遞到test函式的值為[len]int時,函式並未作用到原來的arr1,只是複製了一個新陣列,然後在test函式內對新陣列進行賦值
package main
import "fmt"
func main() {
//var arr1 [5]int
var arr1 = new([5]int)
test(arr1)
fmt.Println("The orignal array is:", arr1)
}
func test(a *[5]int) {
for i, v := range a {
fmt.Printf("Old value is:%d ----> ", v)
a[i] = i
fmt.Printf("New value is:%d\n", a[i])
}
fmt.Println("The array is: ", a)
}
但是如果傳入的值為new[5]int時,test函式就可以直接作用到main中的arr陣列
類似於一維陣列,多維陣列的定義如下
var name [len1][len2]int // e.g. var myArray [2][3]int
多維陣列的賦值,讀取操作:
package main
import (
"fmt"
"strconv" //使用strconv.Itoa將int轉為str
)
func main() {
var myArr [3][4]string
for pos, val := range myArr {
fmt.Printf("pos is %d,type of val is %T\n", pos, val)
for posC, valC := range val {
valC = "row:" + strconv.Itoa(pos) + ",col:" + strconv.Itoa(posC)
fmt.Printf("the value is %s\n", valC)
}
}
}
如果你想定義一個常量陣列,你可以這樣
var myArr = [5]int{1,2,3,4,5}
// or
var myArr = [...]int{1,2,3,4,5}
切片可以理解為長度可以變化的陣列,是陣列的一小段,我們可以如下定義:
var slicename []type = arr[start:end]
//arr為同型別的陣列,start為陣列的起始標,end為陣列的終止下標
或者也可以這樣定義:
slicename := arr[start:end]
package main
import (
"fmt"
)
func main() {
var myArr [7]int
var mySlice []int = myArr[2:4] //定義切片
for i := 0; i < len(myArr); i++ {
myArr[i] = i
}
for i := 0; i < len(mySlice); i++ {
fmt.Printf("Slice at %d is %d\n", i, mySlice[i])
}
fmt.Printf("The length of myArr is %d\n", len(myArr))
fmt.Printf("The length of mySlice is %d\n", len(mySlice))
fmt.Printf("The cap of mySlice is %d\n", cap(mySlice)) //cap方法
fmt.Println("------------------切片擴容--------------------->")
//切片擴容
mySlice = mySlice[:3]
for i := 0; i < len(mySlice); i++ {
fmt.Printf("mySlice at %d is %d\n", i, mySlice[i])
}
fmt.Printf("The length of mySlice is %d\n", len(mySlice))
fmt.Printf("The cap of mySlice is %d\n", cap(mySlice))
//切片,陣列共享資料
mySlice[0] = 100
for pos, val := range myArr {
fmt.Printf("The myArr at %d is %d\n", pos, val)
}
for pos, val := range mySlice {
fmt.Printf("The mySlice at %d is %d\n", pos, val)
}
}
- 切片myArr[2:4]獲取陣列myArr的的myArr[2],myArr[3]但不包含myArr[4],即[2,3]
- go提供了len來獲取切片的長度為2
- cap(mySlice)獲取切片的容量,容量即切片的最大長度,mySlice的cap為3,即從mySlice[2]開始算起,整個myArr的長度
- 切片和陣列共享陣列,修改切片中的數值,陣列的相應值也會發變化
將切片傳遞給函式可參考陣列傳遞
package main
import (
"fmt"
)
func sum(a []int) int {
s := 0
for _, val := range a {
s += val
}
return s
}
func main() {
var myArr = [5]int{0, 1, 2, 3, 4}
fmt.Println("The sum of myArr is ", sum(myArr[:]))
}
上面切片的定義總是依賴陣列,但是如果想單獨定義一個切片的話可以這樣:
var mySlice = make([]type,len,cap) //cap為可選引數,e.g. var mySlice = make([]int,10,20)
//or
var mySlice = new([len]type)[start:end] //e.g. var mySlice = new([20]int)[0:10]
go中的字串是無法修改的,類似於陣列,我們可以字串進行切片,len等常規操作
package main
import (
"fmt"
)
func main() {
var myStr string = "Hello,World"
//len操作
for i := 0; i < len(myStr); i++ {
fmt.Printf("Pos is %d,Val is %c\n", i, myStr[i])
}
//切片操作
mySlice := myStr[:5]
fmt.Printf("mySlice is %s", mySlice)
}
如果我們想對string型別進行更多的操作,如更換其中某個位置的值的話,可以考慮引入一個位元組陣列[]byte
package main
import "fmt"
func main() {
var myStr string = "Hello,World"
//引入一個byte陣列,修改str中的某個位置的值
myByteArr := []byte(myStr)
myByteArr[0] = 'P'
myStr = string(myByteArr)
fmt.Printf("myStr is %s\n", myStr)
//copy操作
myByteArr1 := make([]byte, len(myByteArr))
copy(myByteArr1, myByteArr)
fmt.Printf("myByteArr1 is %s\n", myByteArr1)
//appand操作
myByteArr = append(myByteArr, "..."...)
fmt.Printf("myByteArr is %s\n", myByteArr)
}
在使用append時,當我們插入的是同型別的陣列時,需要在陣列後面加入...,如果插入的是當個值,如append([]int,int)時,插入的int不需要新增...
map類似於python中的dict結構,我們可以這樣定義map
var mapname map[keytype]valuetype // e.g. var myMap map[string]int
//or
var mapname = make(map[keytype]valuetype,cap) // cap可選,e.g. var myMap = make(map[string]int)
map相當於強化版的陣列,普通陣列的下標pos只能是int,而map的下標key可以是string,int,float等型別。
我們在介紹陣列時提到陣列可以使用new定義,但是map中一定別用new定義,因為這樣你只會得到一個空應用的指標
package main
import "fmt"
func main() {
var myMap = map[string]int{"one": 1, "two": 2}
var myMap1 = make(map[int]string) //宣告時不加入cap引數,map的容量動態增長
myMap1[1] = "one" //下標不必從零開始
myMap1[2] = "two"
for key, val := range myMap {
fmt.Printf("myMap key is %s,val is %d\n", key, val)
}
//使用if val,isPresent := myMap[keyname]判斷keyname是否存在,
//如果存在,則ifPresentweitrue,反之則反
if val,isPresent := myMap["two"];isPresent{
fmt.Println(`The valueof "two" in myMap is `,val)
} else {
fmt.Println(`myMap does not contain "two"`)
}
//使用delete(mapname,keyname)刪除
delete(myMap,"two")
fmt.Println(myMap)
}
map的val可以為任意型別,例如函式,空介面型別等
package main
import "fmt"
func main() {
var myMap = map[string]func(int) int{
"func": func(a int) int { return a },
}
//定義一個string:func的map,func接收一個int,並將其返回
fmt.Println(myMap["func"](23))
}
結構體
go中的結構體struct相當於其他語言中類的概念,我們可以這樣定義一個結構體struct
type namestruct struct {
fieldname1 type
fieldname2 type
...
}
var ms = new(namestruct) //使用new返回一個指向結構體的指標*structname
我們可以使用.符來獲取struct中的field, e.g. structname.fieldname
使用fmt.Printf("%v",structname)可以很好的輸出結構體的內容
package main
import "fmt"
type person struct {
name string
age int
}
func main() {
var joey = new(person)
joey.name = "Joey"
joey.age = 23
//或者也可以這樣賦值
// joey := person{name:"Joey",age:23}
fmt.Printf("The name is: %s\n", joey.name)
fmt.Printf("The age is: %d\n", joey.age)
fmt.Printf("The struct is %v\n", joey)
fmt.Println(joey)
}
結構體的繼承的使用內嵌結構體實現
type structname1 struct {
fielName structname //
}
結構體的方法使用結構器實現:
func (recv receiverType) methodName(parameterList) (returnValueList){
...
}
//e.g. func (p *structname) add() int {
return p.x + p.y
}
package main
import "fmt"
//定義一個包含x,y的結構體
type myStruct struct {
x int
y int
}
//定義myStruct1繼承myStruct,再新增一個field
type myStruct1 struct {
myStruct
z int
}
//定義myStruct的一個加法方法
func (p *myStruct) Add() int {
return p.x + p.y
}
//定義myStruct1的一個加法方法
func (p *myStruct1) Add() int {
return p.myStruct.Add() + p.z
}
func main() {
s1 := myStruct{x: 1, y: 2}
fmt.Printf("The add method in myStruct is %d\n", s1.Add())
var s2 = myStruct1{s1, 3}
fmt.Printf("The add method in myStruct1 is %d\n", s2.Add())
}
介面
介面是方法method的集合,我們可以這樣定義一個介面
type Namer interface {
Method1(paramList) returnType
Method2(parmList) returnType
}
//介面裡面只能包含方法
介面的命名採用er結尾。一般來說,一個介面通常包括0到3個方法
package main
import (
"fmt"
"math"
)
//定義一個圓的結構體
type circle struct {
radius float32
}
//定義一個矩形結構體
type rectangle struct {
length, width float32
}
//圓的面積計算方法
func (c circle) Area() float32 {
return math.Pi * c.radius * c.radius
}
//矩形的面積計算方法
func (r *rectangle) Area() float32 {
return r.length * r.width
}
//定義一個介面,裡面包含Area方法
type shaper interface {
Area() float32
}
func main() {
c := circle{3}
r := &rectangle{2, 3}
//定義一個介面型別的陣列
s := []shaper{c, r}
for pos, _ := range s {
fmt.Printf("shape details: %T\n", s[pos])
fmt.Println("Area of this shape is: ", s[pos].Area())
}
}
使用介面可以使得程式更加可讀和靈活,接下來我們用介面來實現整數陣列的排序
package main
import "fmt"
//定義一個整形陣列
type intArray []int
//定義整形陣列的Len方法,該方法返回陣列的長度
func (p intArray) Len() int {
return len(p)
}
//定義陣列的比較方法
func (p intArray) Less(i, j int) bool {
return p[i] < p[j]
}
//定義陣列的交換方法,該方法交換陣列裡面的兩個數
func (p intArray) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
//定義介面,該介面包含intArray的所有方法
type Sorter interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
//定義排序函式,該函式呼叫介面的方法實現
func Sort(data Sorter) {
for pass := 1; pass < data.Len(); pass++ {
for i := 0; i < data.Len()-pass; i++ {
if data.Less(i+1, i) {
data.Swap(i, i+1)
}
}
}
}
func main() {
data := []int{2, 4, 1, 8, 5, 3, 7, 6}
a := intArray(data)
var t Sorter = a
Sort(t)
fmt.Println(data)
}
空介面是不包含任何方法,但是可以被賦予任意型別的值,在go語言中應用廣泛。我們可以這樣定義一個空介面
type Any interface {}
package main
import "fmt"
type person struct {
name string
age int
}
//定義空介面
type Any interface{}
func main() {
var val Any
//賦予int
val = 5
fmt.Printf("The type of val is %T,val of Any is %v\n", val, val)
//賦予string
val = "Apple"
fmt.Printf("The type of val is %T,val of Any is %v\n", val, val)
//賦予person
val = person{"Joey", 23}
fmt.Printf("The type of val is %T,val of Any is %v\n", val, val)
//val.(type)獲取val型別
switch t := val.(type) {
case int:
fmt.Printf("Type int %T\n", t)
case string:
fmt.Printf("Type string %T\n", t)
case bool:
fmt.Printf("Type boolean %T\n", t)
case person:
fmt.Printf("Type person %T\n", t)
default:
fmt.Printf("Unexpected type %T\n", t)
}
}
上面的例子中,我們使用val.(type)方法獲取field的型別。除此之外我們還可以使用reflect反射達到同樣的效果,reflect反射是go的一個內建包,使用時需要先import
package main
import (
"fmt"
"reflect"
)
type rectangle struct {
length int
width int
}
func (p rectangle) Area() int {
return p.length * p.width
}
var secret interface{} = rectangle{2, 3}
func main() {
//使用ValueOf方法獲取空介面的值
val := reflect.ValueOf(secret)
//使用TypeOf方法獲取空介面的型別
typ := reflect.TypeOf(secret)
//對獲取的值使用Kind方法進一步獲取其基本型別
k := val.Kind()
fmt.Println("the val is ", val)
fmt.Println("the type is ", typ)
fmt.Println("The kind is ", k)
//NumField獲取val的field數量,Field(int)可獲取對應下標的val的值
for i := 0; i < val.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, val.Field(i))
}
}
協程與通道
在go語言中,為了能夠並行的執行程式,我們可以使用go關鍵字,e.g.
go func(){}() //開啟一個協程執行匿名函式
//OR
go funcName(paramList) // 開啟一個協程執行函式
觀看以下程式
package main
import (
"fmt"
"time"
)
func printInt() {
for i := 1; i < 6; i++ {
fmt.Println("printInt ---> ", i)
}
time.Sleep(1e9)
}
func printStr() {
s := []byte{'a', 'b', 'c', 'd', 'e'}
for _, i := range s {
fmt.Println("printStr ---> ", string(i))
}
time.Sleep(1e9)
}
func main() {
t1 := time.Now()
go printInt()
go printStr()
time.Sleep(1e9)
fmt.Println(time.Since(t1))
}
上面的例子中,如果main函式中的printInt,printStr函式的呼叫去掉go,那麼整個程式的執行時間為3s。但是由於使用了協程,main,printInt,printStr同時執行,所以程式的執行時間為1s。(實際測試為1s稍多一點)
上面的例子其實存在一個缺陷,如果你嘗試把main函式中的time.Sleep去掉,那麼整個程式將無法按照預期執行。
因為main函式一旦退出,系統並不會等待printInt和printStr執行結束,整個程式馬上結束了。
為了防止這種情況的發生,我們在main中讓程式等待1s,但是這種方式看起來很笨拙,接下來,我們來介紹一種優雅的方式(通道)協調協程和主函式的執行
通道的宣告
var channelName chan type // chan為通道的關鍵字,type為通道可接收的型別,可以是int,string,struct等
當然我們還可以使用make語句定義通道
var channelName = make(chan type,int) //make裡的第一個引數是chan的型別,第二個引數是該通道的容量,e.g. var ch = make(chan string,10)
//如果make的第二個引數int省略的話,預設為1
現在我們知道定義通道方法了,但是要做到協調,我們還需要知道如何進行通道的通訊
var ch = make(chan int)
ch <- 3 //往通道內傳送3
s := <-ch //將通道內的3傳送給s
通道的通訊使用<-符表示,箭頭的方向代表通道的傳送和接收,需要注意的是,一個通道最大能接收的資料量為通道的容量,而且只有當通道內有資料時才能被讀取
package main
import (
"fmt"
"time"
)
func printInt(ch chan bool) {
for i := 1; i < 6; i++ {
fmt.Println("printInt ---> ", i)
}
time.Sleep(1e9)
ch <- true
}
func printStr(ch chan bool) {
s := []byte{'a', 'b', 'c', 'd', 'e'}
for _, i := range s {
fmt.Println("printStr ---> ", string(i))
}
time.Sleep(1e9)
ch <- true
}
func main() {
t1 := time.Now()
chInt := make(chan bool)
chStr := make(chan bool)
go printInt(chInt)
go printStr(chStr)
<-chInt //如果printInt函式中chInt沒有數傳送,則會一直阻塞
<-chStr
fmt.Println(time.Since(t1))
}
使用通道協調main和協程的本質在於通道的阻塞機制。上面的程式main函式中的<-chInt 依賴於協程printInt的ch<-true,否則會一直阻塞。這就相當於main函式需要等待printInt執行結束。
當我們在寫程式中需要使用較多數量的通道時,select語句就變得很有用。select語句類似於switch語句
select {
case u := <- ch1:
...
case v := <- ch2:
...
...
default:
...
}
我們試著將上一個程式使用select語句編寫
package main
import (
"fmt"
"time"
)
func printInt(ch, cs chan bool) {
for i := 1; i < 6; i++ {
fmt.Println("printInt ---> ", i)
}
time.Sleep(1e9)
ch <- true
cs <- true
}
func printStr(ch, cs chan bool) {
s := []byte{'a', 'b', 'c', 'd', 'e'}
for _, i := range s {
fmt.Println("printStr ---> ", string(i))
}
time.Sleep(1e9)
ch <- true
cs <- true
}
func main() {
t1 := time.Now()
chInt := make(chan bool)
chStr := make(chan bool)
chStop := make(chan bool, 2)
go printInt(chInt, chStop)
go printStr(chStr, chStop)
for {
select {
case <-chInt:
fmt.Println("func printInt done!")
case <-chStr:
fmt.Println("func printStr done!")
default:
if len(chStop) == 2 {
fmt.Println(time.Since(t1))
return
}
}
}
}
錯誤處理和測試
go語言沒有try/catch的錯誤處理機制,對於一些普通的錯誤,在設計程式時就將程式的正確執行與否作為返回值,如果返回的值為nil,則證明函式正常執行
通常我們可以使用if語句來判斷
if value,err := func(paramList);nil != nil {
processError()
}
package main
import (
"errors"
"fmt"
)
func divison(dividend float64, divisor float64) (float64, error) {
if divisor == 0 {
return 0, errors.New("The divisor is zero...")
}
return dividend / divisor, nil
}
func main() {
if val, err := divison(3, 0); err != nil {
fmt.Printf("error:%v\n", err)
} else {
fmt.Println("The value is ", val)
}
}
上面的例子中我們用errors.New方法定義了一個error,用以鑑別除數為零的錯誤情況
雖然出現了錯誤,但是上面的程式還是能夠完整的執行,只不過是返回的結果不同。如果我們在執行程式中,碰到了一些嚴重的錯誤,需要讓整個程式停止下來時,可以使用panic
package main
import (
"fmt"
)
func divison(dividend float64, divisor float64) (float64, error) {
if divisor == 0 {
//return 0, errors.New("The divisor is zero...")
panic("The divisor is zero...")
}
return dividend / divisor, nil
}
func main() {
if val, err := divison(3, 0); err != nil {
fmt.Printf("error:%v\n", err)
} else {
fmt.Println("The value is ", val)
}
}
程式一旦觸發panic,會先列印panic方法內的字串,然後標明觸發的位置和資訊
如果需要從panic中恢復,使得程式繼續執行,可以使用recover語句。
package main
import (
"fmt"
)
func divison(dividend float64, divisor float64) (float64, error) {
if divisor == 0 {
//return 0, errors.New("The divisor is zero...")
panic("The divisor is zero...")
}
return dividend / divisor, nil
}
func test() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panic %s\r\n", e)
}
}()
divison(3, 0)
fmt.Println("Test function...")
}
func main() {
fmt.Println("Start main...")
test()
fmt.Println("End main...")
}
recover語句只能和defer搭配使用,defer語句通常和匿名函式配合使用,可以說recover,defer,匿名函式是一套組合
類似於python,在go語言中,我們也可以使用go test工具對程式進行測試。使用go test需要遵循以下幾個規則:
- 引入testing包
- 測試的程式名需要以_test結尾
- _test.go中的測試函式名需要以Test開頭
接下來我們按照上述規則來寫一個例項,新建一個資料夾even,資料夾中新建一個檔案even.go
package even
//判斷一個數為奇偶
func Even(i int) bool {
return i%2 == 0
}
func Odd(i int) bool {
return i%2 != 0
}
接下來在even中再建立一個測試檔案even_test.go
package even
import "testing"
func TestEven(t *testing.T) {
if Even(10) {
t.Log("10 must be even!")
}
if Even(7) {
t.Log("7 is not even")
t.Fail()
}
}
func TestOdd(t *testing.T) {
if !Odd(11) {
t.Log("11 must be odd!")
}
if Odd(10) {
t.Log("10 is not odd!")
t.Fail()
}
}
even_test.go中的函式分別對Even和Odd函式進行測試,Log方法列印錯誤資訊,Fail標記函式失敗,然後繼續接下來的測試
在資料夾所在路徑下,終端執行go test就可以看到測試結果了
本作品採用《CC 協議》,轉載必須註明作者和本文連結