A Tour of Go - Go語(yǔ)言之旅

Hello,世界

這是Go語(yǔ)言官方的一個(gè)簡(jiǎn)明入門(mén)教程沿侈,可以幫助我們快速入門(mén)Go語(yǔ)言:Go語(yǔ)言之旅闯第,它提供了一個(gè)在線編輯、編譯缀拭、運(yùn)行咳短、格式化代碼的平臺(tái)。我們也可以通過(guò)

$ go get -u gitHub.com/Go-zh/tour tour

在自己的電腦上安裝并運(yùn)行此教程的程序蛛淋。
首先讓我們來(lái)看一下作為各種編程語(yǔ)言入門(mén)著名的hello world程序的Go語(yǔ)言版本

package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界")
}

上面的代碼運(yùn)行結(jié)果輸出如下:

Hello, 世界

1 基礎(chǔ)

1.1 包

Go語(yǔ)言程序是由包構(gòu)成的咙好,main()函數(shù)作為執(zhí)行程序的入口,只能包含在main包中褐荷。當(dāng)需要使用外部包的時(shí)候勾效,使用import導(dǎo)入外部包。import可能還會(huì)包含導(dǎo)入路徑诚卸,包名與導(dǎo)入路徑的最后一個(gè)元素一致。當(dāng)需要導(dǎo)入多個(gè)包時(shí)绘迁,使用import加括號(hào)合溺,括號(hào)中包含所需要的所有包名。
示例程序:

package main

import (
    "fmt"
    "math/rand" //導(dǎo)入math/rand包用以調(diào)用它定義的Into()函數(shù)
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10)) //返回10以?xún)?nèi)的隨機(jī)數(shù)(沒(méi)有定義隨機(jī)數(shù)種子缀台,所以每次都返回同一個(gè)數(shù)-偽隨機(jī)數(shù))
}

代碼運(yùn)行結(jié)果:

My favorite number is 1

1.2 導(dǎo)出名

在Go語(yǔ)言的包中棠赛,變量或者函數(shù)如果是以大寫(xiě)字母開(kāi)頭的表明它是可以被其他包訪問(wèn)的,如果不是則只能被包內(nèi)部訪問(wèn)

package main

import (
    "fmt"
    "math"
)

func main() {
    //fmt.Println(math.pi) //這個(gè)例子其實(shí)并不合適math包里并沒(méi)有pi這個(gè)變量膛腐,運(yùn)行時(shí)只會(huì)報(bào)錯(cuò)undefined睛约。
    fmt.Println(math.Pi)
}

1.3 函數(shù)

通常把實(shí)現(xiàn)某個(gè)需要被頻繁訪問(wèn)調(diào)用的代碼封裝成一個(gè)函數(shù),以使代碼變得更簡(jiǎn)潔高效哲身,Go語(yǔ)言定義函數(shù)的方式:

func <func-name> (parameters ) (return values) {
    do something
}

下面的代碼實(shí)現(xiàn)一個(gè)加法函數(shù):

package main

import "fmt"

func add(x int, y int) int { //Go語(yǔ)言支持同類(lèi)型參數(shù)辩涝,省略類(lèi)型符號(hào),x int, y int等價(jià)于x, y int
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

Go語(yǔ)言函數(shù)支持多值返回勘天。

package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("hello", "world") //swap 返回兩個(gè)值交換后的結(jié)果
    fmt.Println(a, b)
}

代碼運(yùn)行結(jié)果:

world hello

1.4 變量

變量聲明格式:

var <var-name> <var-type>

變量初始化:

var <var-name> <var-type> = <var-value>
var <var-name1>, <var-name2>  <var-type> =  <var-value1>, <var-value2>

短變量聲明:Go語(yǔ)言支持不用顯式指出變量的類(lèi)型怔揩,它可以根據(jù)初始化值的類(lèi)型做類(lèi)型自動(dòng)判斷捉邢。但是它的使用有一定限制 - 只能在函數(shù)體內(nèi)部使用,如果在函數(shù)體外部商膊,必須要使用var關(guān)鍵字伏伐。

k := 3 //k會(huì)被自動(dòng)定義為Int類(lèi)型

當(dāng)類(lèi)型推導(dǎo)存在有歧義的可能時(shí),Go語(yǔ)言會(huì)根據(jù)類(lèi)型的初始化值的精度判斷具體類(lèi)型晕拆。

1.5 基本類(lèi)型

變量聲明也可以同導(dǎo)入語(yǔ)句一樣使用分組方式聲明(括號(hào))藐翎,沒(méi)有明確初始化值的變量會(huì)被賦予零值。

  1. bool - 零值:false
  2. string - 零值:""
  3. int(以及各種位數(shù)大小不同的整型派生類(lèi)型)
  4. byte(8位無(wú)符號(hào)整型-uint8)
  5. rune(32位有符號(hào)整型-int32)
  6. float32,float64
  7. complex64, complex128
    數(shù)值類(lèi)型的零值都是0
    Go語(yǔ)言中不存在隱式的類(lèi)型轉(zhuǎn)換实幕,在需要類(lèi)型轉(zhuǎn)換時(shí)必須要顯式地使用轉(zhuǎn)換函數(shù)()吝镣,否則會(huì)編譯報(bào)錯(cuò):
./prog.go:12:15: cannot use f (variable of type float64) as type uint in variable declaration

1.6 常量

常量使用const關(guān)鍵字定義,且不能使用短變量語(yǔ)法進(jìn)行聲明(很容易理解茬缩,不然的話(huà)根短變量會(huì)產(chǎn)生沖突)

2 流程控制語(yǔ)句

2.1 for循環(huán)

Go語(yǔ)言只有for循環(huán)赤惊,且不需要括號(hào)對(duì)循環(huán)的部分進(jìn)行包括
普通for循環(huán):

for i := 1; i < 10; i++ {
    //do something 
}

類(lèi)C語(yǔ)言while循環(huán):

for i < 100 {
    i++
    //do something
}

無(wú)限循環(huán):

for {
    //do something 
}

2.2 條件跳轉(zhuǎn)

if語(yǔ)句

if condition {
    //do something 
}

可以在條件表達(dá)式之前執(zhí)行一個(gè)表達(dá)式,需要用分號(hào)隔開(kāi)凰锡,表達(dá)式變量的作用域僅限if之內(nèi):

if v := math.Pow(x, n); v < lim {
    return v
}

if-else語(yǔ)句
使用else為條件表達(dá)式的結(jié)果相反時(shí)進(jìn)行另一個(gè)操作未舟。

2.3 switch分支跳轉(zhuǎn)

switch相當(dāng)于一連串的if-else語(yǔ)句,它會(huì)從上到下匹配switch表達(dá)式的值掂为,匹配成功就執(zhí)行分支語(yǔ)句塊裕膀。也可以省略switch表達(dá)式,將匹配判斷放到case語(yǔ)句中去執(zhí)行

switch {
    case t.Hour() < 12:
        fmt.Println("Good morning!")
    case t.Hour() < 17:
        fmt.Println("Good afternoon.")
    default:
        fmt.Println("Good evening.")
    }

2.4 defer語(yǔ)句

defer語(yǔ)句會(huì)將其后面的表達(dá)式推遲到外層函數(shù)返回之后才運(yùn)行

package main

import "fmt"

func main() {
    defer fmt.Println("world") //這一段會(huì)等到main()函數(shù)返回之后才執(zhí)行

    fmt.Println("hello")
}

推遲的函數(shù)調(diào)用會(huì)被壓入defer棧勇哗,根據(jù)棧的工作原理昼扛,被推遲的函數(shù)會(huì)按照先進(jìn)后出的順序返回。

package main

import "fmt"

func main() {
    fmt.Println("counting")

    for i := 0; i < 10; i++ {
        defer fmt.Println(i)
    }

    fmt.Println("done")
}

程序運(yùn)行結(jié)果:

counting
done
9
8
7
6
5
4
3
2
1
0

3 指針

指針也是一種變量類(lèi)型欲诺,它存儲(chǔ)的是變量或者值的內(nèi)存地址抄谐。

var p *int

指針的零值是nil,&取址操作符扰法,*解引用操作符

i := 42
p = &i //p是一個(gè)指針保存了整型變量i的內(nèi)存地址
*p = 20 //修改了p指向的內(nèi)存地址出存儲(chǔ)的值

與C語(yǔ)言不同的是 Go沒(méi)有指針運(yùn)算蛹含。(指針自增、自減等)

4 結(jié)構(gòu)體

通過(guò)struct關(guān)鍵來(lái)定義一組字段創(chuàng)建結(jié)構(gòu)體

type Vertex struct {
    x int
    y int
}

結(jié)構(gòu)體成員使用.操作符來(lái)訪問(wèn)塞颁,通過(guò)結(jié)構(gòu)體指針來(lái)引用結(jié)構(gòu)體成員時(shí)可以通過(guò)*解引用

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    p := &v
        (*p).X = 1e9
    // p.X = 1e9 為了簡(jiǎn)化表達(dá)浦箱,Go語(yǔ)言也支持這樣隱式表達(dá),不同于C語(yǔ)言p->X
    fmt.Println(v)
}

5 數(shù)組

定義數(shù)組:

var a [10]int

數(shù)組的大小是固定的祠锣,不能更改酷窥。

6 切片

  • 在類(lèi)似數(shù)組的定義時(shí),不限定它的大小伴网,就構(gòu)成了切片
var s []int 
  • 切片文法
[]bool{true, false, true}

切片的索引上界是它的長(zhǎng)度蓬推,下界是0,使用[n:m]表示子切片時(shí)澡腾,不包括索引值為m的元素拳氢。

  • 切片長(zhǎng)度
len(s)
  • 切片容量
cap(s)
  • 切片的零值是nil
  • 使用make創(chuàng)建切片
a := make ([]int, 5) //創(chuàng)建包含5個(gè)整型零值元素的切片募逞,切片的長(zhǎng)度是5
a := make([]int, 0, 5) //創(chuàng)建包含0個(gè)元素但是容量是5的切片
  • 切片本身也可以做為切片的元素,也就是說(shuō)可以嵌套定義馋评。
  • 可以通過(guò)內(nèi)建的append()函數(shù)向切片追加元素放接,如果切片的容量已滿(mǎn),會(huì)分配一個(gè)更大的數(shù)組留特,返回的切片會(huì)指向新分配的數(shù)組纠脾。但是追加的元素必須要和原切片的類(lèi)型相同。

7 Range

  • range形式的for循環(huán)可以用來(lái)遍歷切片和循環(huán)
package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }
}
  • 可以將索引下標(biāo)或者值賦予_來(lái)忽略它們
for i, _ := range pow
for _, value := range pow

8 映射

  • Go語(yǔ)言映射類(lèi)型就是類(lèi)似字典那樣的鍵值對(duì)蜕青,映射的零值是nil苟蹈,和切片類(lèi)似,可以使用make()方法初始化映射
type Vertex struct {
    Lat, Long float64
}

m := make(map[string]Vertex)
  • 訪問(wèn)映射時(shí)必須要提供key值
  • 映射支持嵌套定義右核,也就是鍵值對(duì)里的值也可以是映射類(lèi)型
  • 修改映射
m[key] = elem //如果key鍵存在慧脱,則更新對(duì)應(yīng)的值為elem,如果不存在則向映射m添加{key:elem}元素
elem = m[key] //獲取映射m中key鍵對(duì)應(yīng)的值賦給elem
delete(m, key) //刪除映射m中key鍵對(duì)應(yīng)的元素
elem, ok = m[key] //如果key鍵存在于映射m中贺喝,則ok=true菱鸥,elem被賦予對(duì)應(yīng)的值;否則ok=false躏鱼,elem賦予對(duì)應(yīng)類(lèi)型的零值
elem, ok := m[key] //如果elem,ok沒(méi)有被聲明則可以使用短變量聲明格式

9 函數(shù)值

Go語(yǔ)言中氮采,函數(shù)也可以作為一種值類(lèi)型,它可以用作函數(shù)的參數(shù)或者返回值

package main

import (
    "fmt"
    "math"
)

func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
}

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    } //hypot被定義為一個(gè)需要傳入兩個(gè)float64類(lèi)型參數(shù)染苛,返回一個(gè)float64類(lèi)型的函數(shù)值類(lèi)型鹊漠,它的定義由math.Sqrt()實(shí)現(xiàn)
    fmt.Println(hypot(5, 12))

    fmt.Println(compute(hypot)) //hypot作為函數(shù)值類(lèi)型也可以作為compute函數(shù)的參數(shù)
    fmt.Println(compute(math.Pow))
}

10 函數(shù)的閉包

待補(bǔ)充

11 方法

  • Go語(yǔ)言沒(méi)有類(lèi)的概念,可以為結(jié)構(gòu)體類(lèi)型定義方法茶行,方法就是帶有指定的接收者參數(shù)的函數(shù)
package main

import (
    "fmt"
    "math"
)

type Vertex struct { //定義結(jié)構(gòu)體Vertex
    X, Y float64
}

func (v Vertex) Abs() float64 { //為結(jié)構(gòu)體Vertex定義方法-方法的接受者必須為Vertex類(lèi)型變量
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := Vertex{3, 4}
    fmt.Println(v.Abs())
}
  • 并不是只能為結(jié)構(gòu)體類(lèi)型聲明方法躯概,也可以指定接受者類(lèi)型為內(nèi)建類(lèi)型,但是內(nèi)建類(lèi)型必須要用type關(guān)鍵字指定別名畔师。話(huà)說(shuō)回來(lái)娶靡,在Go語(yǔ)言中一旦使用type為內(nèi)建類(lèi)型指定別名,這個(gè)別名就不能認(rèn)為是和原內(nèi)建類(lèi)型一樣的去考慮茉唉。
package main

import (
    "fmt"
    "math"
)

type MyFloat float64 //只有為float64類(lèi)型指定別名MyFloat之后才能為其定義方法

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

func main() {
    f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}
  • 方法接受者的類(lèi)型也可以是指針類(lèi)型拉庵,這樣方法就可以讀取和修改原始值淡溯,而不是只在值副本上進(jìn)行操作
package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func main() {
    v := Vertex{3, 4} //用3,4初始化一個(gè)Vertex
    v.Scale(10)//因?yàn)镾cale()方法的接受者是指針類(lèi)型籽孙,所以這里的v是指針奋渔,它可以修改原始值3*10塘雳,4*10揍鸟,
    fmt.Println(v.Abs())//如果修改Scale()方法使方法接受者為Vertex類(lèi)型變量流强,則Scale修改的只是v的副本犀呼,在計(jì)算v.Abs()時(shí)蜡感,仍然用的是原始值3蹬蚁,4
}
  • 在Go函數(shù)中恃泪,函數(shù)參數(shù)如果聲明為指針類(lèi)型必須要傳入指針值,否則編譯會(huì)報(bào)錯(cuò)
package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func Abs(v Vertex) float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func Scale(v *Vertex, f float64) { //如果在這里把*移除犀斋,編譯就會(huì)報(bào)錯(cuò)
    v.X = v.X * f
    v.Y = v.Y * f
}

func main() {
    v := Vertex{3, 4}
    Scale(&v, 10)
    fmt.Println(Abs(v))
}
  • 而在方法定義中贝乎,即使方法接受者被定義為一個(gè)指針,那么在調(diào)用方法的類(lèi)型即使是一個(gè)值也不會(huì)編譯出錯(cuò)
var v Vertex
v.Scale(5)  // OK 因?yàn)镾cale()方法有一個(gè)指針接收者叽粹,Go會(huì)將v.Scale(5)解釋為(&v).Scale(5)
p := &v
p.Scale(10) // OK

這樣做的原因有二:
首先览效,方法能夠修改接受者指向的值
其次,避免在處理大型結(jié)構(gòu)時(shí)虫几,復(fù)制值造成的資源浪費(fèi)

12 接口

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锤灿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辆脸,更是在濱河造成了極大的恐慌但校,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啡氢,死亡現(xiàn)場(chǎng)離奇詭異状囱,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)空执,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)浪箭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人辨绊,你說(shuō)我怎么就攤上這事奶栖。” “怎么了门坷?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵宣鄙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我默蚌,道長(zhǎng)冻晤,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任绸吸,我火速辦了婚禮鼻弧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锦茁。我一直安慰自己攘轩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布码俩。 她就那樣靜靜地躺著度帮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上笨篷,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天瞳秽,我揣著相機(jī)與錄音,去河邊找鬼率翅。 笑死练俐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冕臭。 我是一名探鬼主播痰洒,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浴韭!你這毒婦竟也來(lái)了丘喻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤念颈,失蹤者是張志新(化名)和其女友劉穎泉粉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體榴芳,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嗡靡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窟感。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讨彼。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖柿祈,靈堂內(nèi)的尸體忽然破棺而出哈误,到底是詐尸還是另有隱情,我是刑警寧澤躏嚎,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布蜜自,位于F島的核電站,受9級(jí)特大地震影響卢佣,放射性物質(zhì)發(fā)生泄漏重荠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一虚茶、第九天 我趴在偏房一處隱蔽的房頂上張望戈鲁。 院中可真熱鬧,春花似錦嘹叫、人聲如沸婆殿。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸣皂。三九已至抓谴,卻和暖如春暮蹂,著一層夾襖步出監(jiān)牢的瞬間寞缝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工仰泻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荆陆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓集侯,卻偏偏與公主長(zhǎng)得像被啼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子棠枉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容