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

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

    • 分享

      Go 語法速覽與實踐清單(V0.5)

       quasiceo 2018-08-03

      Go CheatSheet 是對于 Go 學習/實踐過程中的語法與技巧進行盤點,其屬于 Awesome CheatSheet 系列,致力于提升學習速度與研發(fā)效能,即可以將其當做速查手冊,也可以作為輕量級的入門學習資料。 本文參考了許多優(yōu)秀的文章與代碼示范,統(tǒng)一聲明在了 Go Links;如果希望深入了解某方面的內(nèi)容,可以繼續(xù)閱讀 Go 開發(fā):語法基礎與工程實踐,或者前往 coding-snippets/go 查看使用 Go 解決常見的數(shù)據(jù)結構與算法、設計模式、業(yè)務功能方面的代碼實現(xiàn)。

      環(huán)境配置與語法基礎

      可以前往這里下載 Go SDK 安裝包,或者使用 brew 等包管理器安裝。go 命令依賴于 $GOPATH 環(huán)境變量進行代碼組織,多項目情況下也可以使用 ln 進行目錄映射以方便進行項目管理。GOPATH 允許設置多個目錄,每個目錄都會包含三個子目錄:src 用于存放源代碼,pkg 用于存放編譯后生成的文件,bin 用于存放編譯后生成的可執(zhí)行文件。

      環(huán)境配置完畢后,可以使用 go get 獲取依賴,go run 運行程序,go build 來編譯項目生成與包名(文件夾名)一致的可執(zhí)行文件。Golang 1.8 之后支持 dep 依賴管理工具,對于空的項目使用 dep init 初始化依賴配置,其會生成 Gopkg.toml Gopkg.lock vendor/ 這三個文件(夾)。

      我們可以使用 dep ensure -add github.com/pkg/errors 添加依賴,運行之后,其會在 toml 文件中添加如下鎖:

      [[constraint]]
        name = "github.com/pkg/errors"
        version = "0.8.0"

      簡單的 Go 中 Hello World 代碼如下:

      package main
      import "fmt"
      func main() {
          fmt.Println("hello world")
      }

      也可以使用 Beego 實現(xiàn)簡單的 HTTP 服務器:

      package main
      import "github.com/astaxie/beego"
      func main() {
          beego.Run()
      }

      Go 并沒有相對路徑引入,而是以文件夾為單位定義模塊,譬如我們新建名為 math 的文件夾,然后使用 package math 來聲明該文件中函數(shù)所屬的模塊。

      import (
              mongo "mywebapp/libs/mongodb/db" // 對引入的模塊重命名
              _ "mywebapp/libs/mysql/db" // 使用空白下劃線表示僅調(diào)用其初始化函數(shù)
      
      )

      外部引用該模塊是需要使用工作區(qū)間或者 vendor 相對目錄,其目錄索引情況如下:

      cannot find package "sub/math" in any of:
          ${PROJECTROOT}/vendor/sub/math (vendor tree)
          /usr/local/Cellar/go/1.10/libexec/src/sub/math (from $GOROOT)
          ${GOPATH}/src/sub/math (from $GOPATH)

      Go 規(guī)定每個源文件的首部需要進行包聲明,可執(zhí)行文件默認放在 main 包中;而各個包中默認首字母大寫的函數(shù)作為其他包可見的導出函數(shù),而小寫函數(shù)則默認外部不可見的私有函數(shù)。

      表達式與控制流

      變量聲明與賦值

      作為強類型靜態(tài)語言,Go 允許我們在變量之后標識數(shù)據(jù)類型,也為我們提供了自動類型推導的功能。

      // 聲明三個變量,皆為 bool 類型
      var c, python, java bool
      
      // 聲明不同類型的變量,并且賦值
      var i bool, j int = true, 2
      
      // 復雜變量聲明
      var (
          ToBe   bool       = false
          MaxInt uint64     = 1<<64 - 1
          z      complex128 = cmplx.Sqrt(-5 + 12i)
      )
      
      // 短聲明變量
      c, python, java := true, false, "no!"
      
      // 聲明常量
      const constant = "This is a constant"

      在 Go 中,如果我們需要比較兩個復雜對象的相似性,可以使用 reflect.DeepEqual 方法:

      m1 := map[string]int{
          "a":1,
          "b":2,
      }
      m2 := map[string]int{
          "a":1,
          "b":2,
      }
      fmt.Println(reflect.DeepEqual(m1, m2))

      條件判斷

      Go 提供了增強型的 if 語句進行條件判斷:

      // 基礎形式
      if x > 0 {
          return x
      } else {
          return -x
      }
      
      // 條件判斷之前添加自定義語句
      if a := b + c; a < 42 {
          return a
      } else {
          return a - 42
      }
      
      // 常用的類型判斷
      var val interface{}
      val = "foo"
      if str, ok := val.(string); ok {
          fmt.Println(str)
      }

      Go 也支持使用 Switch 語句:

      // 基礎格式
      switch operatingSystem {
      case "darwin":
          fmt.Println("Mac OS Hipster")
          // 默認 break,不需要顯式聲明
      case "linux":
          fmt.Println("Linux Geek")
      default:
          // Windows, BSD, ...
          fmt.Println("Other")
      }
      
      // 類似于 if,可以在條件之前添加自定義語句
      switch os := runtime.GOOS; os {
      case "darwin": ...
      }
      
      // 使用 switch 語句進行類型判斷:
      switch v := anything.(type) {
        case string:
          fmt.Println(v)
        case int32, int64:
          ...
        default:
          fmt.Println("unknown")
      }

      Switch 中也支持進行比較:

      number := 42
      switch {
          case number < 42:
              fmt.Println("Smaller")
          case number == 42:
              fmt.Println("Equal")
          case number > 42:
              fmt.Println("Greater")
      }

      或者進行多條件匹配:

      var char byte = '?'
      switch char {
          case ' ', '?', '&', '=', '#', '+', '%':
              fmt.Println("Should escape")
      }

      循環(huán)

      Go 支持使用 for 語句進行循環(huán),不存在 while 或者 until:

      for i := 1; i < 10; i++ {
      }
      
      // while - loop
      for ; i < 10;  {
      }
      
      // 單條件情況下可以忽略分號
      for i < 10  {
      }
      
      // ~ while (true)
      for {
      }

      我們也可以使用 range 函數(shù),對于 Arrays 與 Slices 進行遍歷:

      // loop over an array/a slice
      for i, e := range a {
          // i 表示下標,e 表示元素
      }
      
      // 僅需要元素
      for _, e := range a {
          // e is the element
      }
      
      // 或者僅需要下標
      for i := range a {
      }
      
      // 定時執(zhí)行
      for range time.Tick(time.Second) {
          // do it once a sec
      }

      Function: 函數(shù)

      定義,參數(shù)與返回值

      // 簡單函數(shù)定義
      func functionName() {}
      
      // 含參函數(shù)定義
      func functionName(param1 string, param2 int) {}
      
      // 多個相同類型參數(shù)的函數(shù)定義
      func functionName(param1, param2 int) {}
      
      // 函數(shù)表達式定義
      add := func(a, b int) int {
          return a + b
      }

      Go 支持函數(shù)的最后一個參數(shù)使用 ... 設置為不定參數(shù),即可以傳入一個或多個參數(shù)值:

      func adder(args ...int) int {
          total := 0
          for _, v := range args { // Iterates over the arguments whatever the number.
              total += v
          }
          return total
      }
      
      adder(1, 2, 3) // 6
      adder(9, 9) // 18
      
      nums := []int{10, 20, 30}
      adder(nums...) // 60

      我們也可以使用 Function Stub 作為函數(shù)參數(shù)傳入,以實現(xiàn)回調(diào)函數(shù)的功能:

      func Filter(s []int, fn func(int) bool) []int {
          var p []int // == nil
          for _, v := range s {
              if fn(v) {
                  p = append(p, v)
              }
          }
          return p
      }

      雖然 Go 不是函數(shù)式語言,但是也可以用其實現(xiàn)柯里函數(shù)(Currying Function):

      func add(x, y int) int {
          return x+ y
      }
      
      func adder(x int) (func(int) int) {
          return func(y int) int {
              return add(x, y)
          }
      }
      
      func main() {
          add3 := adder(3)
          fmt.Println(add3(4))    // 7
      }

      Go 支持多個返回值:

      // 返回單個值
      func functionName() int {
          return 42
      }
      
      // 返回多個值
      func returnMulti() (int, string) {
          return 42, "foobar"
      }
      var x, str = returnMulti()
      
      // 命名返回多個值
      func returnMulti2() (n int, s string) {
          n = 42
          s = "foobar"
          // n and s will be returned
          return
      }
      var x, str = returnMulti2()

      閉包: Closure

      Go 同樣支持詞法作用域與變量保留,因此我們可以使用閉包來訪問函數(shù)定義處外層的變量:

      func scope() func() int{
          outer_var := 2
          foo := func() int { return outer_var}
          return foo
      }

      閉包中并不能夠直接修改外層變量,而是會自動重定義新的變量值:

      func outer() (func() int, int) {
          outer_var := 2
          inner := func() int {
              outer_var += 99
              return outer_var // => 101 (but outer_var is a newly redefined
          }
          return inner, outer_var // => 101, 2 (outer_var is still 2, not mutated by inner!)
      }

      函數(shù)執(zhí)行

      Go 中提供了 defer 關鍵字,允許將某個語句的執(zhí)行推遲到函數(shù)返回語句之前:

      func read(...) (...) {
        f, err := os.Open(file)
        ...
        defer f.Close()
        ...
        return .. // f will be closed

      異常處理

      Go 語言中并不存在 try-catch 等異常處理的關鍵字,對于那些可能返回異常的函數(shù),只需要在函數(shù)返回值中添加額外的 Error 類型的返回值:

      type error interface {
          Error() string
      }

      某個可能返回異常的函數(shù)調(diào)用方式如下:

      import (
          "fmt"
          "errors"
      )
      
      func main() {
          result, err:= Divide(2,0)
      
          if err != nil {
                  fmt.Println(err)
          }else {
                  fmt.Println(result)
          }
      }
      
      func Divide(value1 int,value2 int)(int, error) {
          if(value2 == 0){
              return 0, errors.New("value2 mustn't be zero")
          }
          return value1/value2  , nil
      }

      Go 還為我們提供了 panic 函數(shù),所謂 panic,即是未獲得預期結果,常用于拋出異常結果。譬如當我們獲得了某個函數(shù)返回的異常,卻不知道如何處理或者不需要處理時,可以直接通過 panic 函數(shù)中斷當前運行,打印出錯誤信息、Goroutine 追蹤信息,并且返回非零的狀態(tài)碼:

      _, err := os.Create("/tmp/file")
      if err != nil {
          panic(err)
      }

      數(shù)據(jù)類型與結構

      類型綁定與初始化

      Go 中的 type 關鍵字能夠對某個類型進行重命名:

      // IntSlice 并不等價于 []int,但是可以利用類型轉換進行轉換
      type IntSlice []int
      a := IntSlice{1, 2}

      可以使用 T(v) 或者 obj.(T) 進行類型轉換,obj.(T) 僅針對 interface{} 類型起作用:

      t := obj.(T) // if obj is not T, error
      t, ok := obj.(T) // if obj is not T, ok = false
      
      // 類型轉換與判斷
      str, ok := val.(string);

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

      interface {} // ~ java Object
      bool // true/false
      string
      int8  int16  int32  int64
      int // =int32 on 32-bit, =int64 if 64-bit OS
      uint8 uint16 uint32 uint64 uintptr
      uint
      byte // alias for uint8
      rune // alias for int32, represents a Unicode code point
      float32 float64

      字符串

      // 多行字符串聲明
      hellomsg := `
       "Hello" in Chinese is 你好 ('Ni Hao')
       "Hello" in Hindi is ?????? ('Namaste')
      `

      格式化字符串:

      fmt.Println("Hello, 你好, ??????, Привет, ???") // basic print, plus newline
      p := struct { X, Y int }{ 17, 2 }
      fmt.Println( "My point:", p, "x coord=", p.X ) // print structs, ints, etc
      s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // print to string variable
      
      fmt.Printf("%d hex:%x bin:%b fp:%f sci:%e",17,17,17,17.0,17.0) // c-ish format
      s2 := fmt.Sprintf( "%d %f", 17, 17.0 ) // formatted print to string variable

      序列類型

      Array 與 Slice 都可以用來表示序列數(shù)據(jù),二者也有著一定的關聯(lián)。

      Array

      其中 Array 用于表示固定長度的,相同類型的序列對象,可以使用如下形式創(chuàng)建:

      [N]Type
      [N]Type{value1, value2, ..., valueN}
      
      // 由編譯器自動計算數(shù)目
      [...]Type{value1, value2, ..., valueN}

      其具體使用方式為:

      // 數(shù)組聲明
      var a [10]int
      
      // 賦值
      a[3] = 42
      
      // 讀取
      i := a[3]
      
      // 聲明與初始化
      var a = [2]int{1, 2}
      a := [2]int{1, 2}
      a := [...]int{1, 2}

      Go 內(nèi)置了 len 與 cap 函數(shù),用于獲取數(shù)組的尺寸與容量:

      var arr = [3]int{1, 2, 3}
      arr := [...]int{1, 2, 3}
      
      len(arr) // 3
      cap(arr) // 3

      不同于 C/C++ 中的指針(Pointer)或者 Java 中的對象引用(Object Reference),Go 中的 Array 只是值(Value)。這也就意味著,當進行數(shù)組拷貝,或者函數(shù)調(diào)用中的參數(shù)傳值時,會復制所有的元素副本,而非僅僅傳遞指針或者引用。顯而易見,這種復制的代價會較為昂貴。

      Slice

      Slice 為我們提供了更為靈活且輕量級地序列類型操作,可以使用如下方式創(chuàng)建 Slice:

      // 使用內(nèi)置函數(shù)創(chuàng)建
      make([]Type, length, capacity)
      make([]Type, length)
      
      // 聲明為不定長度數(shù)組
      []Type{}
      []Type{value1, value2, ..., valueN}
      
      // 對現(xiàn)有數(shù)組進行切片轉換
      array[:]
      array[:2]
      array[2:]
      array[2:3]

      不同于 Array,Slice 可以看做更為靈活的引用類型(Reference Type),它并不真實地存放數(shù)組值,而是包含數(shù)組指針(ptr),len,cap 三個屬性的結構體。換言之,Slice 可以看做對于數(shù)組中某個段的描述,包含了指向數(shù)組的指針,段長度,以及段的最大潛在長度,其結構如下圖所示:

      group 2

      // 創(chuàng)建 len 為 5,cap 為 5 的 Slice
      s := make([]byte, 5)
      
      // 對 Slice 進行二次切片,此時 len 為 2,cap 為 3
      s = s[2:4]
      
      // 恢復 Slice 的長度
      s = s[:cap(s)]

      需要注意的是, 切片操作并不會真實地復制 Slice 中值,只是會創(chuàng)建新的指向原數(shù)組的指針,這就保證了切片操作和操作數(shù)組下標有著相同的高效率。不過如果我們修改 Slice 中的值,那么其會 真實修改底層數(shù)組中的值,也就會體現(xiàn)到原有的數(shù)組中:

      d := []byte{'r', 'o', 'a', 'd'}
      e := d[2:]
      // e == []byte{'a', 'd'}
      e[1] = 'm'
      // e == []byte{'a', 'm'}
      // d == []byte{'r', 'o', 'a', 'm'}

      Go 提供了內(nèi)置的 append 函數(shù),來動態(tài)為 Slice 添加數(shù)據(jù),該函數(shù)會返回新的切片對象,包含了原始的 Slice 中值以及新增的值。如果原有的 Slice 的容量不足以存放新增的序列,那么會自動分配新的內(nèi)存:

      // len=0 cap=0 []
      var s []int
      
      // len=1 cap=2 [0]
      s = append(s, 0)
      
      // len=2 cap=2 [0 1]
      s = append(s, 1)
      
      // len=5 cap=8 [0 1 2 3 4]
      s = append(s, 2, 3, 4)
      
      // 使用 ... 來自動展開數(shù)組
      a := []string{"John", "Paul"}
      b := []string{"George", "Ringo", "Pete"}
      a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])"
      // a == []string{"John", "Paul", "George", "Ringo", "Pete"}

      我們也可以使用內(nèi)置的 copy 函數(shù),進行 Slice 的復制,該函數(shù)支持對于不同長度的 Slice 進行復制,其會自動使用最小的元素數(shù)目。同時,copy 函數(shù)還能夠自動處理使用了相同的底層數(shù)組之間的 Slice 復制,以避免額外的空間浪費。

      func copy(dst, src []T) int
      
      // 申請較大的空間容量
      t := make([]byte, len(s), (cap(s)+1)*2)
      copy(t, s)
      s = t

      映射類型

      var m map[string]int
      m = make(map[string]int)
      m["key"] = 42
      
      // 刪除某個鍵
      delete(m, "key")
      
      // 測試該鍵對應的值是否存在
      elem, has_value := m["key"]
      
      // map literal
      var m = map[string]Vertex{
          "Bell Labs": {40.68433, -74.39967},
          "Google":    {37.42202, -122.08408},
      }

      Struct & Interface: 結構體與接口

      Struct: 結構體

      Go 語言中并不存在類的概念,只有結構體,結構體可以看做屬性的集合,同時可以為其定義方法。

      // 聲明結構體
      type Vertex struct {
          // 結構體的屬性,同樣遵循大寫導出,小寫私有的原則
          X, Y int
          z bool
      }
      
      // 也可以聲明隱式結構體
      point := struct {
          X, Y int
      }{1, 2}
      
      // 創(chuàng)建結構體實例
      var v = Vertex{1, 2}
      
      // 讀取或者設置屬性
      v.X = 4;
      
      // 顯示聲明鍵
      var v = Vertex{X: 1, Y: 2}
      
      // 聲明數(shù)組
      var v = []Vertex{{1,2},{5,2},{5,5}}

      方法的聲明也非常簡潔,只需要在 func 關鍵字與函數(shù)名之間聲明結構體指針即可,該結構體會在不同的方法間進行復制:

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

      對于那些需要修改當前結構體對象的方法,則需要傳入指針:

      func (v *Vertex) add(n float64) {
          v.X += n
          v.Y += n
      }
      var p *Person = new(Person) // pointer of type Person

      Pointer: 指針

      // p 是 Vertex 類型
      p := Vertex{1, 2}  
      
      // q 是指向 Vertex 的指針
      q := &p
      
      // r 同樣是指向 Vertex 對象的指針
      r := &Vertex{1, 2}
      
      // 指向 Vertex 結構體對象的指針類型為 *Vertex
      var s *Vertex = new(Vertex)

      Interface: 接口

      Go 允許我們通過定義接口的方式來實現(xiàn)多態(tài)性:

      // 接口聲明
      type Awesomizer interface {
          Awesomize() string
      }
      
      // 結構體并不需要顯式實現(xiàn)接口
      type Foo struct {}
      
      // 而是通過實現(xiàn)所有接口規(guī)定的方法的方式,來實現(xiàn)接口
      func (foo Foo) Awesomize() string {
          return "Awesome!"
      }
      type Shape interface {
         area() float64
      }
      
      func getArea(shape Shape) float64 {
         return shape.area()
      }
      
      type Circle struct {
         x,y,radius float64
      }
      
      type Rectangle struct {
         width, height float64
      }
      
      func(circle Circle) area() float64 {
         return math.Pi * circle.radius * circle.radius
      }
      
      func(rect Rectangle) area() float64 {
         return rect.width * rect.height
      }
      
      func main() {
         circle := Circle{x:0,y:0,radius:5}
         rectangle := Rectangle {width:10, height:5}
      
         fmt.Printf("Circle area: %f\n",getArea(circle))
         fmt.Printf("Rectangle area: %f\n",getArea(rectangle))
      }
      //Circle area: 78.539816
      //Rectangle area: 50.000000

      慣用的思路是先定義接口,再定義實現(xiàn),最后定義使用的方法:

      package animals
      
      type Animal interface {
          Speaks() string
      }
      
      // implementation of Animal
      type Dog struct{}
      func (a Dog) Speaks() string { return "woof" }
      
      /** 在需要的地方直接引用 **/
      
      package circus
      
      import "animals"
      
      func Perform(a animal.Animal) { return a.Speaks() }

      Go 也為我們提供了另一種接口的實現(xiàn)方案,我們可以不在具體的實現(xiàn)處定義接口,而是在需要用到該接口的地方,該模式為:

      func funcName(a INTERFACETYPE) CONCRETETYPE

      定義接口:

      package animals
      
      type Dog struct{}
      func (a Dog) Speaks() string { return "woof" }
      
      /** 在需要使用實現(xiàn)的地方定義接口 **/
      package circus
      
      type Speaker interface {
          Speaks() string
      }
      
      func Perform(a Speaker) { return a.Speaks() }

      Embedding

      Go 語言中并沒有子類繼承這樣的概念,而是通過嵌入(Embedding)的方式來實現(xiàn)類或者接口的組合。

      // ReadWriter 的實現(xiàn)需要同時滿足 Reader 與 Writer
      type ReadWriter interface {
          Reader
          Writer
      }
      
      // Server 暴露了所有 Logger 結構體的方法
      type Server struct {
          Host string
          Port int
          *log.Logger
      }
      
      // 初始化方式并未受影響
      server := &Server{"localhost", 80, log.New(...)}
      
      // 卻可以直接調(diào)用內(nèi)嵌結構體的方法,等價于 server.Logger.Log(...)
      server.Log(...)
      
      // 內(nèi)嵌結構體的名詞即是類型名
      var logger *log.Logger = server.Logger

      并發(fā)編程

      Goroutines

      Goroutines 是輕量級的線程,可以參考并發(fā)編程導論一文中的進程、線程與協(xié)程的討論;Go 為我們提供了非常便捷的 Goroutines 語法:

      // 普通函數(shù)
      func doStuff(s string) {
      }
      
      func main() {
          // 使用命名函數(shù)創(chuàng)建 Goroutine
          go doStuff("foobar")
      
          // 使用匿名內(nèi)部函數(shù)創(chuàng)建 Goroutine
          go func (x int) {
              // function body goes here
          }(42)
      }

      Channels

      信道(Channel)是帶有類型的管道,可以用于在不同的 Goroutine 之間傳遞消息,其基礎操作如下:

      // 創(chuàng)建類型為 int 的信道
      ch := make(chan int)
      
      // 向信道中發(fā)送值
      ch <- 42
      
      // 從信道中獲取值
      v := <-ch
      
      // 讀取,并且判斷其是否關閉
      v, ok := <-ch
      
      // 讀取信道,直至其關閉
      for i := range ch {
          fmt.Println(i)
      }

      譬如我們可以在主線程中等待來自 Goroutine 的消息,并且輸出:

      // 創(chuàng)建信道
      messages := make(chan string)
      
      // 執(zhí)行 Goroutine
      go func() { messages <- "ping" }()
      
      // 阻塞,并且等待消息
      msg := <-messages
      
      // 使用信道進行并發(fā)地計算,并且阻塞等待結果
      c := make(chan int)
      go sum(s[:len(s)/2], c)
      go sum(s[len(s)/2:], c)
      x, y := <-c, <-c // 從 c 中接收

      如上創(chuàng)建的是無緩沖型信道(Non-buffered Channels),其是阻塞型信道;當沒有值時讀取方會持續(xù)阻塞,而寫入方則是在無讀取時阻塞。我們可以創(chuàng)建緩沖型信道(Buffered Channel),其讀取方在信道被寫滿前都不會被阻塞:

      ch := make(chan int, 100)
      
      // 發(fā)送方也可以主動關閉信道
      close(ch)

      Channel 同樣可以作為函數(shù)參數(shù),并且我們可以顯式聲明其是用于發(fā)送信息還是接收信息,從而增加程序的類型安全度:

      // ping 函數(shù)用于發(fā)送信息
      func ping(pings chan<- string, msg string) {
          pings <- msg
      }
      
      // pong 函數(shù)用于從某個信道中接收信息,然后發(fā)送到另一個信道中
      func pong(pings <-chan string, pongs chan<- string) {
          msg := <-pings
          pongs <- msg
      }
      
      func main() {
          pings := make(chan string, 1)
          pongs := make(chan string, 1)
          ping(pings, "passed message")
          pong(pings, pongs)
          fmt.Println(<-pongs)
      }

      同步

      同步,是并發(fā)編程中的常見需求,這里我們可以使用 Channel 的阻塞特性來實現(xiàn) Goroutine 之間的同步:

      func worker(done chan bool) {
          time.Sleep(time.Second)
          done <- true
      }
      
      func main() {
          done := make(chan bool, 1)
          go worker(done)
      
          // 阻塞直到接收到消息
          <-done
      }

      Go 還為我們提供了 select 關鍵字,用于等待多個信道的執(zhí)行結果:

      // 創(chuàng)建兩個信道
      c1 := make(chan string)
      c2 := make(chan string)
      
      // 每個信道會以不同時延輸出不同值
      go func() {
          time.Sleep(1 * time.Second)
          c1 <- "one"
      }()
      go func() {
          time.Sleep(2 * time.Second)
          c2 <- "two"
      }()
      
      // 使用 select 來同時等待兩個信道的執(zhí)行結果
      for i := 0; i < 2; i++ {
          select {
          case msg1 := <-c1:
              fmt.Println("received", msg1)
          case msg2 := <-c2:
              fmt.Println("received", msg2)
          }
      }

      Web 編程

      HTTP Server

      package main
      
      import (
          "fmt"
          "net/http"
      )
      
      // define a type for the response
      type Hello struct{}
      
      // let that type implement the ServeHTTP method (defined in interface http.Handler)
      func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          fmt.Fprint(w, "Hello!")
      }
      
      func main() {
          var h Hello
          http.ListenAndServe("localhost:4000", h)
      }
      
      // Here's the method signature of http.ServeHTTP:
      // type Handler interface {
      //     ServeHTTP(w http.ResponseWriter, r *http.Request)
      // }

      Beego

      利用 Beego 官方推薦的 bee 命令行工具,我們可以快速創(chuàng)建 Beego 項目,其目錄組織方式如下:

      quickstart
      ├── conf
      │   └── app.conf
      ├── controllers
      │   └── default.go
      ├── main.go
      ├── models
      ├── routers
      │   └── router.go
      ├── static
      │   ├── css
      │   ├── img
      │   └── js
      ├── tests
      │   └── default_test.go
      └── views
          └── index.tpl

      在 main.go 文件中,我們可以啟動 Beego 實例,并且調(diào)用路由的初始化配置文件:

      package main
      
      import (
              _ "quickstart/routers"
              "github.com/astaxie/beego"
      )
      
      func main() {
              beego.Run()
      }

      而在路由的初始化函數(shù)中,我們會聲明各個路由與控制器之間的映射關系:

      package routers
      
      import (
              "quickstart/controllers"
              "github.com/astaxie/beego"
      )
      
      func init() {
              beego.Router("/", &controllers.MainController{})
      }

      也可以手動指定 Beego 項目中的靜態(tài)資源映射:

      beego.SetStaticPath("/down1", "download1")
      beego.SetStaticPath("/down2", "download2")

      在具體的控制器中,可以設置返回數(shù)據(jù),或者關聯(lián)的模板名:

      package controllers
      
      import (
              "github.com/astaxie/beego"
      )
      
      type MainController struct {
              beego.Controller
      }
      
      func (this *MainController) Get() {
              this.Data["Website"] = ""
              this.Data["Email"] = "astaxie@gmail.com"
              this.TplNames = "index.tpl" // version 1.6 use this.TplName = "index.tpl"
      }

      DevPractics: 開發(fā)實踐

      文件讀寫

      import (
          "io/ioutil"
      )
      ...
      datFile1, errFile1 := ioutil.ReadFile("file1")
      if errFile1 != nil {
          panic(errFile1)
      }
      ...

      測試

      VSCode 可以為函數(shù)自動生成基礎測試用例,并且提供了方便的用例執(zhí)行與調(diào)試的功能。

      /** 交換函數(shù) */
      func swap(x *int, y *int) {
          x, y = y, x
      }
      
      /** 自動生成的測試函數(shù) */
      func Test_swap(t *testing.T) {
          type args struct {
              x *int
              y *int
          }
          tests := []struct {
              name string
              args args
          }{
              // TODO: Add test cases.
          }
          for _, tt := range tests {
              t.Run(tt.name, func(t *testing.T) {
                  swap(tt.args.x, tt.args.y)
              })
          }
      }

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多