乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      從 0 到 1 學習 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)建簡單、可靠、高效的軟件。”
      — GoLang

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

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

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

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

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

      入門

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

      package main

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

      工作空間(Workspace)

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

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

      將 GOPATH 設置到預期目錄?,F(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 關鍵字引入一個包。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

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

      var a = 1

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

      message := 'hello world'

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

      var b, c int = 23

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

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

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

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

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

      布爾型變量的關鍵字是 bool 。

      Go 還支持以 complex64 和 complex128 聲明的復雜數(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ù)組具有在聲明中定義的固定長度,因此不能進行擴展。數(shù)組聲明為:

      var a [5]int

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

      var multiD [2][3]int

      當數(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)

      增加切片容量的另一種方法是使用復制功能。只需創(chuàng)建另一個具有更大容量的切片,并將原始切片復制到新創(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)關鍵字。 單個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. 傳遞值時實際上復制了值,這意味著更多的內(nèi)存通過指針傳遞

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

      例:

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

      注意:嘗試本文示例代碼時,不要忘記將其包含在main包中,并在需要時導入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 關鍵字后跟函數(shù)名來定義 Go 函數(shù)。函數(shù)所需的參數(shù)需要根據(jù)其數(shù)據(jù)類型定義,最后是返回的數(shù)據(jù)類型。

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

      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ù)進行分組,我們會定義一個人的屬性,其中可能包括姓名,年齡,性別。 可以使用以下語法定義結(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)用該方法。 請注意,接收器是指針。 使用指針,我們傳遞對值的引用,因此如果我們對方法進行任何更改,它將反映在接收器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包。

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

      安裝包

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

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

      自定義一個

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

      > mkdir custom_package> cd custom_package

      要創(chuàng)建自定義包,我們需要首先使用我們需要的包名創(chuàng)建一個文件夾。假設我們正在建立一個包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)在需要安裝包,以便可以導入和使用它。所以讓我們安裝它:

      > 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)在,我們可以導入我們創(chuàng)建的包person并使用函數(shù)Description。請注意,我們在包中創(chuàng)建的函數(shù)secretName將無法訪問。在Go中,以大寫字母開頭的方法名稱將是私有的。

      包文檔

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

      godoc person Description

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

      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映射到的響應類型struct的地址。 請注意,json:“page”將頁面鍵映射到struct中的PageNumber鍵。

      錯誤處理

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

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

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

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

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

      當我們編寫自己的函數(shù)時,有些情況下我們會遇到錯誤??梢栽阱e誤對象的幫助下返回這些錯誤:

      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ù)都可能存在錯誤。 這些錯誤永遠不應該被忽略,并且總是在我們稱之為函數(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í)行該行。 當我們需要在函數(shù)結(jié)束時執(zhí)行某些操作時,也可以使用Defer,例如關閉文件。

      并發(fā)

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

      Go routine

      Go routine是可以與另一個函數(shù)并行或同時運行的函數(shù)。 創(chuàng)建Go routine非常簡單。 只需在函數(shù)前添加關鍵字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)學習了 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ù)前進。構(gòu)思一個小應用,然后開始構(gòu)建之。

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多