日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

從 0 到 1 學(xué)習(xí) Go 語言

 西北望msm66g9f 2019-01-27

圖片:Gopher 吉祥物和舊 logo

簡要介紹下 Go(Golang) 語言。Go 語言是由谷歌工程師 Robert Griesemer、Rob Pike 和 Ken Thompson 創(chuàng)造的一種靜態(tài)類型編譯語言。首個開源版本發(fā)布于2012年3月。

“Go 是一種開源編程語言,可以輕松構(gòu)建簡單、可靠、高效的軟件?!?br>— GoLang

在諸多編程語言中,解決給定問題的方法有很多,程序員往往需要花很多時間思考關(guān)于解決問題的最佳方法。

但是,Go 相信更少的功能 —— 只有一種正確的方法來解決問題。

這節(jié)省了開發(fā)人員的時間,并使大型代碼庫易于維護(hù)。 Go 中沒有 maps 和 filters 等“富有表現(xiàn)力”的功能。

“當(dāng)你需要增加功能的豐富性,往往伴隨著開銷的增加” 
- Rob Pike

Golang 最新發(fā)布的 logo: https://blog./go-brand

入門

Go 程序由包組成。編譯器會將 main 包編譯成可執(zhí)行程序,而不是共享庫。main 包是整個應(yīng)用的入口。main 包的定義如下:

package main

接下來,咱們在 Go 的工作空間中創(chuàng)建一個叫做 main.go 的文件,來實現(xiàn)一個簡單的 hello world 例子。

工作空間(Workspace)

Go 的工作空間可以通過環(huán)境變量 GOPATH 定義。

你需要在這個工作空間中編寫你自己的代碼。Go 為搜索 GOPATH 以及 GOROOT 指定的目錄中的所有包,GOROOT 變量是在安裝 GO 時設(shè)置的,也就是 go 語言安裝的目錄。

將 GOPATH 設(shè)置到預(yù)期目錄。現(xiàn)在,增加一個文件夾 ~/workspace

# export env
export GOPATH=~/workspace

# go inside the workspace directory
cd ~/workspace

接下來在這個工作空間中創(chuàng)建 main.go ,寫入下面的代碼。

Hello World!

package mainimport 'fmt')func main(){  fmt.Println('Hello World!')}

在上面的示例代碼中,fmt 是 Go 的一個內(nèi)置包,主要實現(xiàn)格式化 I/O 的功能。

在 Go 語言中,通過 import 關(guān)鍵字引入一個包。func main 是可執(zhí)行代碼的入口。fmt 中的 Println 函數(shù)實現(xiàn) “hello world” 的打印。

下面嘗試運行這個文件。我們可以通過兩種方法運行一個 Go 命令。我們知道 Go 是一個編譯性語言,所以,在執(zhí)行之前我們先來編譯它。

> go build main.go

上面的命令就生成了一個可執(zhí)行文件 main ,接下來我們運行這個文件:

> ./main # Hello World!

接下來看另外一種更加簡單的執(zhí)行方式。go run 命令將編譯步驟抽象。那么,通過下面的命令就可以執(zhí)行這個程序。

go run main.go # Hello World!

注意:可以使用 https://play. 嘗試上面的代碼。

變量

Go 的變量必須顯式聲明。Go 是靜態(tài)類型的語言,也就是說,在變量聲明時要檢查變量類型??梢匀缦侣暶饕粋€變量:

var a int

在這種情況下,變量的值會被設(shè)為0。也可以通過下面的語法聲明變量并初始化為不同的值:

var a = 1

在這里,變量自動被判斷為一個整形變量。我們可以通過簡化形式來聲明變量:

message := 'hello world'

也可以在一行聲明多個變量:

var b, c int = 23

數(shù)據(jù)類型

和其他編程語言一樣,Go 語言也有不同的數(shù)據(jù)類型。接下來就來看一下:

數(shù)字(Number)、字符串(String)和布爾(Boolean)

可支持?jǐn)?shù)字存儲類型有 int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr…

字符串以字節(jié)序列的形式存儲。用string關(guān)鍵字表示和聲明。

布爾型變量的關(guān)鍵字是 bool 。

Go 還支持以 complex64 和 complex128 聲明的復(fù)雜數(shù)字類型。

var a bool = true
var b 
int = 1
var c string = 'hello world'
var d float32 = 1.222
var x complex128 = cmplx.Sqrt(-5 + 12i)

數(shù)組、切片、集合

數(shù)組是相同數(shù)據(jù)類型的元素序列。數(shù)組具有在聲明中定義的固定長度,因此不能進(jìn)行擴展。數(shù)組聲明為:

var a [5]int

數(shù)組也可以是多維的。我們可以使用以下格式創(chuàng)建它們:

var multiD [2][3]int

當(dāng)數(shù)組的值在運行時更改時,數(shù)組限制了這種情況。 數(shù)組也不提供獲取子數(shù)組的能力。 為此,Go 有一個名為 slices 的數(shù)據(jù)類型。 切片存儲一系列元素,可以隨時擴展。 切片聲明類似于數(shù)組聲明 - 沒有定義容量:

var b []int

這將創(chuàng)建一個零容量和零長度的切片。切片也可以定義容量和長度。我們可以使用以下語法:

numbers := make([]int,5,10)

這里,切片的初始長度為5,容量為10。

切片是數(shù)組的抽象。切片使用數(shù)組作為底層結(jié)構(gòu)。切片包含三個組件:容量,長度和指向底層數(shù)組的指針,如下圖所示:

圖片來源: https://blog./go-slices-usage-and-internals

通過使用append或copy函數(shù)可以增加切片的容量。 append函數(shù)可以為數(shù)組的末尾添加值,并在需要時增加容量。

numbers = append(numbers, 1234)

增加切片容量的另一種方法是使用復(fù)制功能。只需創(chuàng)建另一個具有更大容量的切片,并將原始切片復(fù)制到新創(chuàng)建的切片:

// create a new slicenumber2 := make([]int15)// copy the original slice to new slicecopy(number2, number)

我們可以創(chuàng)建切片的子切片。這可以使用以下命令完成:

// initialize a slice with 4 len and values
number2 = []int{1,2,3,4}fmt.Println(numbers) // -> [1 2 3 4]// create sub slices
slice1 := number2[2:]fmt.Println(slice1) // -> [3 4]
slice2 := number2[:3]fmt.Println(slice2) // -> [1 2 3]
slice3 := number2[1:4]fmt.Println(slice3) // -> [2 3 4]

集合是Go中的數(shù)據(jù)類型,它將鍵映射到值。我們可以使用以下命令定義映射:

var m map[string]int

這里m是新的集合變量,它的鍵為字符串,值為整數(shù)。我們可以輕松地將鍵和值添加到地圖中:

// adding key/value
m['clearity'] = 2
m['simplicity'] = 3
// printing the values
fmt.Println(m['clearity']) // -> 2
fmt.Println(m['simplicity']) // -> 3

數(shù)據(jù)類型轉(zhuǎn)換

一種數(shù)據(jù)類型可以通過類型轉(zhuǎn)換得到另一種數(shù)據(jù)類型。我們來看一個簡單的例子:

a := 1.1b := int(a)fmt.Println(b)//-> 1

不是所有的數(shù)據(jù)類型都可以轉(zhuǎn)換為別的數(shù)據(jù)類型。必須確保兩種數(shù)據(jù)類型之間的兼容性。

條件語句

if else

對于條件判斷,我們可以像如下的例子那樣使用 if-else 語句。要確保大括號和條件語句在同一行。

if num := 9; num <><>

switch case

Switch cases 有助于組織多個條件語句。以下示例顯示了一個簡單的 switch case 語句:

i := 2switch i {case 1: fmt.Println('one')case 2: fmt.Println('two')default: fmt.Println('none')}

循環(huán)語句

Go有一個循環(huán)關(guān)鍵字。 單個for循環(huán)命令有助于實現(xiàn)不同類型的循環(huán):

i := 0sum := 0for i <>

上面的示例類似于C中的while循環(huán)。對于for循環(huán),也可以使用常見的for語句

sum := 0for i := 0; i <>

Go中的無限循環(huán):

for {}

指針

Go提供指針。指針是保存值的地址的地方。指針由*定義。根據(jù)數(shù)據(jù)類型定義指針。例:

ar ap *int

這里ap是指向整數(shù)類型的指針。 &運算符可用于獲取變量的地址。

a := 12ap = &a

可以使用*運算符訪問指針的值指針:

fmt.Println(*ap)// => 12

在將結(jié)構(gòu)作為參數(shù)傳遞時或在為已定義類型聲明方法時,通常首選指針

  1. 傳遞值時實際上復(fù)制了值,這意味著更多的內(nèi)存通過指針傳遞

  2. 函數(shù)更改的值將反映在方法/函數(shù)調(diào)用者中。

例:

func increment(i *int) {  *i++}func main() {  i := 10  increment(&i)  fmt.Println(i)}//=> 11

注意:嘗試本文示例代碼時,不要忘記將其包含在main包中,并在需要時導(dǎo)入fmt或其他包,如上面第一個main.go示例所示。

函數(shù)

主函數(shù)中定義的主要功能是執(zhí)行程序的入口點,可以定義和使用更多功能。讓我們看一個簡單的例子:

func add(a int, b int) int {  c := a + b  return c}func main() {  fmt.Println(add(2, 1))}//=> 3

正如我們在上面的例子中所看到的,使用 func 關(guān)鍵字后跟函數(shù)名來定義 Go 函數(shù)。函數(shù)所需的參數(shù)需要根據(jù)其數(shù)據(jù)類型定義,最后是返回的數(shù)據(jù)類型。

函數(shù)的返回也可以在函數(shù)中預(yù)定義:

func add(a int, b int) (c int) {  c = a + b  return}func main() {  fmt.Println(add(2, 1))}//=> 3

這里c被定義為返回變量。 因此,定義的變量c將自動返回,而無需在結(jié)尾的return語句中定義。

您還可以從使用逗號分隔返回值的單個函數(shù)返回多個值。

func add(a int, b int) (int, string) {  c := a + b  return c, 'successfully added'}func main() {  sum, message := add(2, 1)  fmt.Println(message)  fmt.Println(sum)}

方法,結(jié)構(gòu)和接口

Go不是一個完全面向?qū)ο蟮恼Z言,但是對于結(jié)構(gòu),接口和方法,它有很多面向?qū)ο蟮闹С趾透杏X。

結(jié)構(gòu)

結(jié)構(gòu)是不同字段的類型集合。 結(jié)構(gòu)用于將數(shù)據(jù)分組在一起。 例如,如果我們想要對Person類型的數(shù)據(jù)進(jìn)行分組,我們會定義一個人的屬性,其中可能包括姓名,年齡,性別。 可以使用以下語法定義結(jié)構(gòu):

type person struct {  name string  age int  gender string}

在定義了人類型結(jié)構(gòu)的情況下,現(xiàn)在讓我們創(chuàng)建一個人:

//way 1: specifying attribute and valuep = person{name: 'Bob', age: 42, gender: 'Male'}//way 2: specifying only valueperson{'Bob', 42, 'Male'}

我們可以用點(.)輕松訪問這些數(shù)據(jù)

p.name//=> Bobp.age//=> 42p.gender//=> Male

您還可以使用其指針直接訪問結(jié)構(gòu)的屬性

pp = &person{name: 'Bob', age: 42, gender: 'Male'}pp.name//=> Bob

方法

方法是具有接收器的特殊類型的功能。接收器既可以是值,也可以是指針。讓我們創(chuàng)建一個名為describe的方法,它具有我們在上面的例子中創(chuàng)建的接收器類型:

package mainimport 'fmt'// struct definationtype person struct {  name   string  age    int  gender string}// method definationfunc (p *person) describe() {  fmt.Printf('%v is %v years old.', p.name, p.age)}func (p *person) setAge(age int) {  p.age = age}func (p person) setName(name string) {  p.name = name}func main() {  pp := &person{name: 'Bob', age: 42, gender: 'Male'}  pp.describe()  // => Bob is 42 years old  pp.setAge(45)  fmt.Println(pp.age)  //=> 45  pp.setName('Hari')  fmt.Println(pp.name)  //=> Bob}

正如我們在上面的例子中所看到的,現(xiàn)在可以使用點運算符作為pp.describe來調(diào)用該方法。 請注意,接收器是指針。 使用指針,我們傳遞對值的引用,因此如果我們對方法進(jìn)行任何更改,它將反映在接收器pp中。它也不會創(chuàng)建對象的新副本,這樣可以節(jié)省內(nèi)存。

請注意,在上面的示例中,age的值已更改,而name的值未更改,因為方法setName屬于receiver類型,而setAge屬于pointer類型。

接口

Go接口是方法的集合。接口有助于將類型的屬性組合在一起。我們以接口動物為例:

type animal interface {  description() string}

這里的動物是一種接口類型。現(xiàn)在讓我們創(chuàng)建兩種不同類型的動物來實現(xiàn)動物接口類型:

package mainimport (  'fmt')type animal interface {  description() string}type cat struct {  Type  string  Sound string}type snake struct {  Type      string  Poisonous bool}func (s snake) description() string {  return fmt.Sprintf('Poisonous: %v', s.Poisonous)}func (c cat) description() string {  return fmt.Sprintf('Sound: %v', c.Sound)}func main() {  var a animal  a = snake{Poisonous: true}  fmt.Println(a.description())  a = cat{Sound: 'Meow!!!'}  fmt.Println(a.description())}//=> Poisonous: true//=> Sound: Meow!!!

在main函數(shù)中,我們創(chuàng)建了一個動物類型的變量a。 我們?yōu)閯游锓峙渖吆拓堫愋停⑹褂肞rintln打印a.description。 由于我們以不同的方式實現(xiàn)了兩種類型(貓和蛇)中描述的方法,我們可以打印出動物的描述。

包 (Package)

我們在Go中編寫所有代碼。主程序包是程序執(zhí)行的入口點。 Go中有很多內(nèi)置包。我們一直使用的最著名的是fmt包。

“在主要機制中使用程序包進(jìn)行大規(guī)模編程,并且可以將大型項目分成更小的部分?!?- 羅伯特格里塞默

安裝包

go get // examplego get github.com/satori/go.uuid

我們安裝的軟件包保存在GOPATH env中,這是我們的工作目錄。您可以通過cd $GOPATH/pkg進(jìn)入我們的工作目錄中的pkg包管理文件夾。

自定義一個

讓我們開始創(chuàng)建一個自定義包文件目錄:

> mkdir custom_package> cd custom_package

要創(chuàng)建自定義包,我們需要首先使用我們需要的包名創(chuàng)建一個文件夾。假設(shè)我們正在建立一個包person。為此,我們在custom_package文件夾中創(chuàng)建一個名為person的文件夾:

> mkdir person> cd person

現(xiàn)在讓我們在這個文件夾中創(chuàng)建一個文件person.go。

package personfunc Description(name string) string {  return 'The person name is: ' + name}func secretName(name string) string {  return 'Do not share'}

我們現(xiàn)在需要安裝包,以便可以導(dǎo)入和使用它。所以讓我們安裝它:

> go install

現(xiàn)在讓我們回到custom_package文件夾并創(chuàng)建一個main.go文件

package mainimport(  'custom_package/person'  'fmt')func main(){   p := person.Description('Milap')  fmt.Println(p)}// => The person name is: Milap

現(xiàn)在,我們可以導(dǎo)入我們創(chuàng)建的包person并使用函數(shù)Description。請注意,我們在包中創(chuàng)建的函數(shù)secretName將無法訪問。在Go中,以大寫字母開頭的方法名稱將是私有的。

包文檔

Go內(nèi)置了對包文檔的支持。運行以下命令以生成文檔:

godoc person Description

這將為我們的包person生成Description函數(shù)的文檔。要查看文檔,請使用以下命令運行Web服務(wù)器:

godoc -http=':8080'

現(xiàn)在轉(zhuǎn)到URL http://localhost:6060/pkg /并查看我們剛剛創(chuàng)建的包的文檔。

Go中的一些內(nèi)置包

fmt

該包實現(xiàn)了格式化的I/O功能。我們已經(jīng)使用該包打印到stdout。

JSON

Go中另一個有用的包是json包。這有助于編碼/解碼JSON。讓我們舉一個例子來編碼/解碼一些json:

編碼

package mainimport (  'fmt'  'encoding/json'func main(){  mapA := map[string]int{'apple': 5, 'lettuce': 7}  mapB, _ := json.Marshal(mapA)  fmt.Println(string(mapB))}

解碼

package mainimport (  'fmt'  'encoding/json')type response struct {  PageNumber int `json:'page'`  Fruits []string `json:'fruits'`}func main(){  str := `{'page': 1, 'fruits': ['apple', 'peach']}`  res := response{}  json.Unmarshal([]byte(str), &res)  fmt.Println(res.PageNumber)}//=> 1

在使用unmarshal解碼json字節(jié)時,第一個參數(shù)是json字節(jié),第二個參數(shù)是我們希望json映射到的響應(yīng)類型struct的地址。 請注意,json:“page”將頁面鍵映射到struct中的PageNumber鍵。

錯誤處理

錯誤是程序的意外和意外結(jié)果。 假設(shè)我們正在對外部服務(wù)進(jìn)行API調(diào)用。 此API調(diào)用可能成功或可能失敗。 當(dāng)存在錯誤類型時,可以識別Go程序中的錯誤。 我們來看看這個例子:

resp, err := http.Get('http:///')

這里對錯誤對象的API調(diào)用可能通過或可能失敗。我們可以檢查錯誤是否為nil,并相應(yīng)地處理響應(yīng):

package mainimport (  'fmt'  'net/http')func main(){  resp, err := http.Get('http:///')  if err != nil {    fmt.Println(err)    return  }  fmt.Println(resp)}

從函數(shù)返回自定義錯誤

當(dāng)我們編寫自己的函數(shù)時,有些情況下我們會遇到錯誤。可以在錯誤對象的幫助下返回這些錯誤:

func Increment(n int) (int, error) {  if n < 0 {      return error object    return nil, errors.new('math: cannot process negative number')  }  return (n + 1), nil}func main() {  num :="">

使用Go構(gòu)建的大多數(shù)軟件包或我們使用的外部軟件包都有一種錯誤處理機制。 所以我們調(diào)用的任何函數(shù)都可能存在錯誤。 這些錯誤永遠(yuǎn)不應(yīng)該被忽略,并且總是在我們稱之為函數(shù)的地方優(yōu)雅地處理,就像我們在上面的例子中所做的那樣。

Panic

Panic是一種未經(jīng)處理的異常,在程序執(zhí)行期間突然遇到。 在Go中,Panic不是處理程序中異常的理想方式。 建議使用錯誤對象。 發(fā)生Panic時,程序執(zhí)行停止。 Panic之后執(zhí)行的事情就是defer。

Defer

Defer總是在函數(shù)結(jié)束時執(zhí)行。

//Gopackage mainimport 'fmt'func main() {    f()    fmt.Println('Returned normally from f.')}func f() {    defer func() {        if r := recover(); r != nil {            fmt.Println('Recovered in f', r)        }    }()    fmt.Println('Calling g.')    g(0)    fmt.Println('Returned normally from g.')}func g(i int) {    if i > 3 {        fmt.Println('Panicking!')        panic(fmt.Sprintf('%v', i))    }    defer fmt.Println('Defer in g', i)    fmt.Println('Printing in g', i)    g(i + 1)}

在上面的例子中,我們使用panic()執(zhí)行程序。 正如您所注意到的,有一個defer語句,它將使程序在程序執(zhí)行結(jié)束時執(zhí)行該行。 當(dāng)我們需要在函數(shù)結(jié)束時執(zhí)行某些操作時,也可以使用Defer,例如關(guān)閉文件。

并發(fā)

Go是建立在并發(fā)性的基礎(chǔ)上的。 Go中的并發(fā)可以通過輕量級線程的Go例程來實現(xiàn)。

Go routine

Go routine是可以與另一個函數(shù)并行或同時運行的函數(shù)。 創(chuàng)建Go routine非常簡單。 只需在函數(shù)前添加關(guān)鍵字Go,我們就可以使它并行執(zhí)行。 Go routine非常輕量級,因此我們可以創(chuàng)建數(shù)千個例程。 讓我們看一個簡單的例子:

package mainimport (  'fmt'  'time')func main() {  go c()  fmt.Println('I am main')  time.Sleep(time.Second * 2)}func c() {  time.Sleep(time.Second * 2)  fmt.Println('I am concurrent')}//=> I am main//=> I am concurrent

正如您在上面的示例中所看到的,函數(shù)c是一個Go routine ,它與主Go線程并行執(zhí)行。 有時我們想要在多個線程之間共享資源。 Go更喜歡不與另一個線程共享變量,因為這會增加死鎖和資源等待的可能性。 還有另一種在Go routine 之間共享資源的方法:via go channels。

通道

我們可以使用通道在兩個Go例程之間傳遞數(shù)據(jù)。在創(chuàng)建頻道時,必須指定頻道接收的數(shù)據(jù)類型。讓我們創(chuàng)建一個字符串類型的簡單通道,如下所示:

c := make(chan string)

使用此通道,我們可以發(fā)送字符串類型數(shù)據(jù)。我們都可以在此頻道中發(fā)送和接收數(shù)據(jù):

package mainimport 'fmt'func main(){  c := make(chan string)  go func(){ c <><-c  fmt.println(msg)} =="">'hello'

接收方通道等待發(fā)送方向通道發(fā)送數(shù)據(jù)。

單向通道

在某些情況下,我們希望 Go routine 通過通道接收數(shù)據(jù)但不發(fā)送數(shù)據(jù),反之亦然。為此,我們還可以創(chuàng)建單向通道。讓我們看一個簡單的示例:

package mainimport ( 'fmt')func main() { ch := make(chan string)  go sc(ch) fmt.Println(<><><>

在上面的例子中,sc 是一個 Go routine ,它只能向通道發(fā)送消息但不能接收消息。

使用select為Go例程組織多個通道

函數(shù)可能有多個通道正在等待。為此,我們可以使用select語句。讓我們看一個更清晰的例子:

package mainimport ( 'fmt' 'time')func main() { c1 := make(chan string) c2 := make(chan string) go speed1(c1) go speed2(c2) fmt.Println('The first to arrive is:') select { case s1 := <><><><>

在上面的例子中,main正在等待兩個通道c1和c2。使用select case語句打印主函數(shù),消息從通道發(fā)送,無論它先收到哪個。

緩沖通道

有些情況下我們需要向通道發(fā)送多個數(shù)據(jù)。您可以為此創(chuàng)建緩沖通道。使用緩沖通道,接收器在緩沖區(qū)已滿之前不會收到消息。我們來看看這個例子:

package mainimport 'fmt'func main(){  ch := make(chan string, 2)  ch <><><>

為什么Golang成功了?

簡單…?— ?Rob-pike

好極了!

我們已經(jīng)學(xué)習(xí)了 Go 語言的一些主要組件及功能。

  1. 變量,數(shù)據(jù)類型

  2. 數(shù)組分片及 map

  3. 函數(shù)

  4. 循環(huán)及條件語句

  5. 指針

  6. 方法,結(jié)構(gòu)和接口

  7. 錯誤處理

  8. 并發(fā) ——? Go routine 及通道

恭喜,你對 Go 已經(jīng)有不錯的理解了。

最富有成效的一天是丟棄1000行代碼。

— Ken Thompson

不要止步于此。繼續(xù)前進(jìn)。構(gòu)思一個小應(yīng)用,然后開始構(gòu)建之。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多