1.main包的唯一性
傳統(tǒng)語言中對主入口的要求是main函數(shù),如c++/java等糊肠,只需要保證這點即可擂啥,但是在go中還需要保證main包的唯一性。
如下才避,在一個main包定義如下函數(shù)
package main
import "fmt"
func func1(){
fmt.Println("test func1")
}
然后在另一個main包的main函數(shù)中如下調(diào)用
package main
import "fmt"
func main(){
//嘗試1-嘗試調(diào)用同目錄下另一個main包中的函數(shù)
//func1()
}
報錯如下:
common_point\test_main1.go:63:2: undefined: func1
可以看到橱夭,兩個main包實際上是相互不可見的,對于自己來說都是唯一的桑逝。需要提下的是棘劣,實際項目中,可以同一目錄下包含多個main包楞遏,只要不相互調(diào)用茬暇,go build/run xxx指明對應(yīng)main包文件編譯運行即可,這樣處理的目的在于使項目結(jié)構(gòu)都清晰寡喝,同時兼容一個項目存在多個入口的情況糙俗。
2.如何跳出for select 循環(huán)
通常在for循環(huán)中,使用break可以跳出循環(huán)预鬓,但是注意在go語言中巧骚,for select配合時,break并不能跳出循環(huán)格二。
如下代碼:
func testSelectFor(chExit chan bool){
for {
select {
case v, ok := <-chExit:
if !ok {
fmt.Println("close channel 1", v)
break
}
fmt.Println("ch1 val =", v)
}
}
fmt.Println("exit testSelectFor")
}
如下調(diào)用:
//嘗試2 select for 跳出循環(huán)
c := make(chan bool)
go testSelectFor(c)
c <- true
c <- false
close(c)
time.Sleep(time.Duration(2) * time.Second)
運行結(jié)果如下劈彪,可以看到break無法跳出循環(huán):
...
close channel 1 false
close channel 1 false
close channel 1 false
close channel 1 false
...
為了解決這個問題,需要設(shè)置標(biāo)簽顶猜,break 標(biāo)簽或goto 便簽即可跳出循環(huán)沧奴,如下兩種方法均可。
func testSelectFor2(chExit chan bool){
EXIT:
for {
select {
case v, ok := <-chExit:
if !ok {
fmt.Println("close channel 2", v)
break EXIT//goto EXIT2
}
fmt.Println("ch2 val =", v)
}
}
//EXIT2:
fmt.Println("exit testSelectFor2")
}
同樣調(diào)用长窄,輸出結(jié)果如下:
ch2 val = true
ch2 val = false
close channel 2 false
exit testSelectFor2
3.如何在切片中查找
go中使用sort.searchXXX方法在排序好的切片中查找指定的方法滔吠,但是其返回結(jié)果很奇怪纲菌,返回是對應(yīng)的查找元素不存在時待插入的位置下標(biāo)(元素插入在返回下標(biāo)前)。如下調(diào)用:
//嘗試3 search查找返回值
s := []string{"ab", "ac", "ac", "bb", "bb", "ee"}
fmt.Println("s=", s)
fmt.Println(sort.SearchStrings(s, "aa"))
fmt.Println(sort.SearchStrings(s, "ac"))
fmt.Println(sort.SearchStrings(s, "ad"))
fmt.Println(sort.SearchStrings(s, "ff"))
返回結(jié)果如下:
s= [ab ac ac bb bb ee]
0
1
3
6
可以看到屠凶,單獨根據(jù)返回值沒法判斷對應(yīng)元素是否存在驰后,封裝如下函數(shù):
func IsExist(s []string, t string) (int, bool) {
iIndex := sort.SearchStrings(s, t)
bExist := iIndex!=len(s) && s[iIndex]==t
return iIndex, bExist
}
這里用返回的下標(biāo)取值再和原來值對比下,即可判斷對應(yīng)元素是否存在矗愧。
fmt.Println(IsExist(s, "aa"))
fmt.Println(IsExist(s, "ac"))
fmt.Println(IsExist(s, "ad"))
fmt.Println(IsExist(s, "ff"))
/*out
0 false
1 true
3 false
6 false*/
4.如何初始化帶嵌套結(jié)構(gòu)的結(jié)構(gòu)體
go的哲學(xué)是組合優(yōu)于繼承灶芝,使用struct嵌套即可完成組合,內(nèi)嵌的結(jié)構(gòu)體屬性就像外層結(jié)構(gòu)的屬性即可唉韭,可以直接調(diào)用夜涕;但是注意初始化外層結(jié)構(gòu)體時必須指定內(nèi)嵌結(jié)構(gòu)體名稱的結(jié)構(gòu)體初始化,如下看到s1方式報錯属愤,s2方式正確女器。
type stPeople struct {
Gender bool
Name string
}
type stStudent struct {
stPeople
Class int
}
//嘗試4 嵌套結(jié)構(gòu)的初始化表達式
//var s1 = stStudent{false, "JimWen", 3}
var s2 = stStudent{stPeople{false, "JimWen"}, 3}
fmt.Println(s2.Gender, s2.Name, s2.Class)
5.切片和數(shù)組
go中沒有特別復(fù)雜的數(shù)據(jù)結(jié)構(gòu),核心就是數(shù)組(array)和map住诸,切片(slice)是基于數(shù)組的驾胆。很多人容易混淆array和slice的關(guān)系,這里著重說下兩者的聯(lián)系和區(qū)別以及應(yīng)用場合贱呐。
先看二者的初始化方法,如下:
//slice和數(shù)組初始化方法
var a0 [5]int
a1 := [5]int{}
a2 := [5]int{1,2,3}
a3 := [...]int{1,2,3}
a4 := [5]int{1,2,3,4,5}
fmt.Println(a0, a1, a2, a3, a4)
var s0 []int
s1 := a4[0:3]
s2 := []int{1,2,3}
s3 := make([]int, 2, 3)
fmt.Println(s0==nil, s1, s2, s3)
輸出如下
[0 0 0 0 0] [0 0 0 0 0] [1 2 3 0 0] [1 2 3] [1 2 3 4 5]
true [1 2 3] [1 2 3] [0 0]
可以看到丧诺,array可以如下初始化:
1.單獨聲明長度,會自動填充0值奄薇,如 var a0 [5]int
2.也可以初始化時賦值驳阎,末尾沒有填滿的填充0值,如a2 := [5]int{1,2,3}
3.如果不指定長度還可以自動計算馁蒂,如a3 := [...]int{1,2,3}
而slice不關(guān)聯(lián)數(shù)組是即為nil(如var s0 []int)呵晚,稱為nil切片。slice要想不為空沫屡,必須和數(shù)組關(guān)聯(lián)饵隙,有如下幾種方法:
1.引用已有數(shù)組,如s1 := a4[0:3]
2.初始化表達式賦值谁鳍,會默認生成底層數(shù)組癞季,然后引用它,如s2 := []int{1,2,3}
3.使用make生成底層數(shù)組倘潜,數(shù)組填充0值绷柒,然后引用它,如s3 := make([]int, 2, 3)
除了引用數(shù)組的情況涮因,slice和array初始化很容易區(qū)別废睦,array必須指定長度(具體數(shù)字或...),而slice不指定長度养泡。
前面說了slice必須關(guān)聯(lián)數(shù)組嗜湃,實際上看下二者的內(nèi)存結(jié)構(gòu)就都明白了奈应,如下:
array在內(nèi)存中是連續(xù)的塊,而slice底層也是數(shù)組购披,只不過加了一個頭杖挣,分別包含數(shù)組起始地址、slice長度刚陡、slice容量惩妇,數(shù)組起始地址和slice長度決定了slice引用的數(shù)組范圍,slice容量決定了append操作時是否開辟新的底層數(shù)組筐乳。
如下操作:
//slice以數(shù)組為基準(zhǔn)
v0 := [4]int{-10, 20, 30, 40}
s := v0[1:3]
fmt.Println(v0, s, &v0[1], &s[0])
//s[3] = 10 panic
s = append(s, 80)
fmt.Println(v0, s, &v0[1], &s[0])
s = append(s, 90)
fmt.Println(v0, s, &v0[1], &s[0])
結(jié)果:
[-10 20 30 40] [20 30] 0xc04200a488 0xc04200a488
[-10 20 30 80] [20 30 80] 0xc04200a488 0xc04200a488
[-10 20 30 80] [20 30 80 90] 0xc04200a488 0xc042008330
一開始s指向v0的第一個元素歌殃,包含兩個元素,slice長度為2蝙云,slice容量剩余數(shù)組元素長度3氓皱。打印結(jié)果可以看到,此時s-0和v0-1是同一個元素勃刨,即slice是指向已有array的波材。
然后append一個80,此時可以看到底層數(shù)組也發(fā)生了改變身隐,這是因為slice的容量為3各聘,當(dāng)前數(shù)組還夠用,所以直接用了當(dāng)前數(shù)組抡医,此時s-0和v0-1是同一個元素,即slice還是指向已有array的早敬。
然后再append一個90忌傻,此時可以看到此時s-0和v0-1不再是同一個元素,即slice指向了新的底層數(shù)組搞监,因為原有數(shù)組已經(jīng)不夠用了水孩,所以新生成數(shù)組。
一定要注意此處的邏輯琐驴,否則很容易不小心修改了底層數(shù)組或想修改底層數(shù)組而生成了新數(shù)組俘种。
那么為什么要同時又數(shù)組和切片呢,在我看來绝淡,數(shù)組是為了提供一種底層C數(shù)組的能力宙刘,而切片相當(dāng)于一種容器迭代器,可以很方便的實現(xiàn)動態(tài)語言的易操作特性牢酵,同時結(jié)合二者可以實現(xiàn)高性能和易用性兼得悬包。
明白了這些,看下如下問題馍乙,如下給函數(shù)changeValue1傳遞一個數(shù)組布近,修改數(shù)組元素值垫释,然后打印,發(fā)現(xiàn)并沒有變化撑瞧。實際上go中變量賦值都是拷貝棵譬,要想實現(xiàn)改變可以使用數(shù)組指針changeValue2或切片changeValue3,他們依然是拷貝预伺,只不過拷貝的是地址订咸,但是指向的依然是底層數(shù)組,所以能夠成功改變數(shù)組元素值扭屁,這樣就保證了go中邏輯的一致性算谈。
//數(shù)組
func changeValue1(v [5]int) {
v[0] = 100
}
//數(shù)組指針
func changeValue2(v *[5]int) {
(*v)[0] = 100
}
//切片
func changeValue3(v []int) {
v[0] = 100
}
// 傳遞數(shù)組參數(shù)
v1 := [5]int{-10, 20, 30, 40, 50}
changeValue1(v1)
v2 := [5]int{-10, 20, 30, 40, 50}
changeValue2(&v2)
v3 := [5]int{-10, 20, 30, 40, 50}
changeValue3(v3[0:])
fmt.Println(v1, v2, v3)
//輸出
[-10 20 30 40 50] [100 20 30 40 50] [100 20 30 40 50]
同樣需要注意的是for range遍歷切片的時候,返回的是拷貝料滥,要想改變對應(yīng)的元素值必須使用索引來改變原來的值然眼,如下:
//for range 對數(shù)組/切片為拷貝
v4 := [5]int{-10, 20, 30, 40, 50}
for _,v := range v4{
v = v*2 //不會改變原來值
}
for k,v := range v4{
fmt.Println(k,v)
}
for k,v := range v4{
v4[k] = v*2 //改變原來值
}
for k,v := range v4{
fmt.Println(k,v)
}
5.map結(jié)構(gòu)查找是否存在鍵值
在其他語言中,鍵值不存在直接應(yīng)用會報異常葵腹,但是在go語言中會返回一個0值高每,因此可以如下兩種方法判斷鍵值是否存在:
package main
import "fmt"
func main(){
m := map[int]string{1:"aaa", 2:"bbb", 3:"ccc", 4:"ddd", 5:"eee"}
fmt.Println(m)
v0, exist0 := m[5]
if exist0{
fmt.Println("exist key 5", v0)
} else{
fmt.Println("not exist key 5")
}
v1 := m[5]
if v1!=""{
fmt.Println("exist key 5", v0)
} else{
fmt.Println("not exist key 5")
}
}
演示代碼下載鏈接
文章來源:http://blog.csdn.net/wenzhou1219
感謝作者:wenzhou1219