1. 程式人生 > >go語言基本語法

go語言基本語法

Go語言

一、特點

1、函數語言程式設計   閉包

2、工程化   資源管理,錯誤處理,測試文件,

3、併發程式設計   goroutine和channel  排程器。

4、介面程式設計, interface

5、全新的靜態型別開發語言

6、更豐富的內建型別slice

7、錯誤處理:
defer, panic和recover

 

 

 

 

 

 

二、語法

Package宣告:表示go程式碼所屬的包。建立一個名字為main的包,在該包中包含一個叫做main()的函式。無參,也沒有定義返回值。

 

宣告以後是import語句,引入需要的模組。

 

需要使println()函式所以引入的是fmt

 

函式定義使用func開頭。

 

所有Go函式(包括在物件程式設計中會提到的型別成員函式)以關鍵字func開頭。一個常規的 函式定義包含以下部分: func 函式名(引數列表)(返回值列表) { // 函式體 } 對應的一個例項如下: func Compute(value1 int, value2 float64)(result float64, err error) { // 函式體 } Go支援多個返回值。以上的示例函式Compute()返回了兩個值,一個叫result,另一個是 err。並不是所有返回值都必須賦值。在函式返回時沒有被明確賦值的返回值都會被設定為預設 值,比如result會被設為0.0,err會被設為nil。

 

三、定義變數

package main

import "fmt"

var(
   a=3
   b=4
   c=2
   d=true
)
func variableZeroValue(){
   var a int
   var s string
   fmt.Printf("%d %q\n", a, s)

}
func  variableInitialValue()  {
   var

a, b int = 3, 4
   var s string = "abc"
  
fmt.Println(a, b, s)
}
func variableDecouments(){
   var a, b, c, d = 1, 3, true, "def"
  
fmt.Println(a,b,c,d)
}
func  variableShorters()  {
   a, b, c ,d := true, 0, 3, 5
   fmt.Println(a, b, c, d)
}

func main()  {
   fmt.Println("hello world")
   variableZeroValue()
   variableInitialValue()
   variableDecouments()
   variableShorters()
   fmt.Println(a, b, c, d)
}

 

 

 

:= 只能在函式內使用

 

四、內建變數型別

1、bool,string

2、(u)int無符號整數,不加u是有符號整數。

Int32,int16,int8,int64,uintptr指標

3、byets,rune字元型

4、浮點型float32,float64,complex64, complex128負數

cmplx.Pow(math.E, 1i * math.Pi)+1

 

5、型別轉換是強制的

 

 

func euler(){

   fmt.Println(cmplx.Pow(math.E, 1i * math.Pi)+1)
   c := 3 + 4i
   fmt.Println(cmplx.Abs(c))
}

 

五、常量

1、普通的定義

func consts(){
   const filename = "abc"
   const a
, b = 3, 4
   fmt.Println(filename, a, b)
}

 

定義在函式內和函式外是一樣的,都可以定義

2、列舉定義

普通型別就是自己定義

 

Iota是自增型別

 

func enums(){
   const (cpp  = iota
   python
   golang
   javascript
)
   fmt.Println(cpp, python, golang, javascript)
}

 

 

 

六、條件語句

1、if

package main

import (
   "fmt"
   "io/ioutil"
)

//func main()  {
// const filename  = "abc.txt"
// contents, err := ioutil.ReadFile(filename)
// if err != nil{
//    fmt.Println(err)
// }else {
//    fmt.Println("%s\n", contents)
// }
//}

 

 

func main()  {
   const filename  = "abc.txt"
   if
contents, err := ioutil.ReadFile(filename); err == nil{
      fmt.Println(string(contents))
   }else {
      fmt.Println("cannot print file contents", err)
     
   }

}

 

直接父類加

2、switch

func grade(source int) string{
   g := ""
   switch 
{
   case source < 0 || source > 100:
      panic(fmt.Sprintf("wrong score: %d", source))
   case source < 60:
      g = "f"
   case
source < 80:
      g = "c"
   case
source < 900:
      g = "b"
   case
source <= 100:
      g = "a"
  
}
   return g
}

 

 

panic異常捕獲語句

switch不需要break,自動有的break

 

3、for迴圈

func sums(){
   sum := 0
   for i := 1; i <=100; i++{
      sum += i
   }
   fmt.Println(sum)
}

 

for 什麼不加的話就是死迴圈,不用的是while

 

 

七、函式

func div(a, b int)(q, r int){
   return a / b, a % b
}

 

 

函式名(引數,引數型別)(返回值,返回值型別)

 

 

可變引數只有一個就是..int

函式作為引數

 

 

 

八、指標

只有值傳遞一種方式。

*a ,*b,指標

&a,&b取地址

第一種實現

func swap(a, b *int)  {
   *a, *b = *b, *a
}

 

a, b := 2, 4
swap(&a, &b)
fmt.Println(a, b)

 

第二種實現方式:

func swaps(a, b int) (int, int) {
   return b, a
}

九、陣列

package main

import "fmt"

func
main(){
   var arr1 [5] int
   arr2 := [3]int{1, 3 ,5}
   arr3 := [...]int{2, 4, 6, 8, 10}
   var grid[4][5] int
   fmt.Println(arr1, arr2, arr3, grid)
   for i, value := range arr3{
      fmt.Println(i, value)
   }
}

 

定義了必須使用,要不然就使用_忽略變數

 

利用range。

意義明顯。

 

 

陣列是值型別,值型別會進行相應的拷貝檔案

package main

import "fmt"
func
printarray(arr [5]int){
   for i, v := range arr{
      fmt.Println(i, v)
   }

}

 

func main(){
   var arr1 [5] int
   arr2 := [3]int{1, 3 ,5}
   arr3 := [...]int{2, 4, 6, 8, 10}
   var grid[4][5] int
   fmt.Println(arr1, arr2, arr3, grid)
   //for i, value := range arr3{
      //fmt.Println(i, value)
   //}
  
printarray(arr3)
   }

 

var arr[5] int 指定的是陣列的長度和值得型別

陣列作為函式的引數穿進去的時候就會拷貝陣列

一般不會直接使用陣列

 

十、切片slice

package main

import "fmt"

func
main()  {
   arr := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
   s := arr[2:6]
   s1 := s[3:5]
   fmt.Println(s)
   fmt.Println(s1)
}

 

s1 因為知道其索引腳標,所以能夠取出所在的值。因為其知道s的值,向後拓展只要不是超過s的長度即可

 

 

 

可以進行append,進行寫入相關資料。

 

 

 

十一、Map

Map[k]v,map[k1]map[k2]v

建立make ,map    m2 := make(map[string]int)      m := map[string]string

遍歷

查詢值

刪除

 

 

Map的key:map使用的是雜湊表,必須可以比較相等。

 

除了slice,map,function的內建型別都可以作為key,

Struct型別不包括上述欄位,也可以作為key

 

package main

import "fmt"

func
main()  {
   m := map[string]string{    //no1
     
"name":"mouse",
      "course":"golang",
      "site":"imocc",
      "quality":"notbad",
   }
   m2 := make(map[string]int)   // no2   == empty map
  
var m3 map[string]int      //no3   == nil
  
fmt.Println(m, m2, m3)
   for k := range m{       //遍歷
     
fmt.Println(k)

   }
   for k,v := range m{     //遍歷
     
fmt.Println(k,v)
   }
   courname := m["course"]     //索引查詢,通過k查詢v。如果不存在則是顯示flase
  
fmt.Println(courname)
   name, ok := m["course"]
   fmt.Println(name, ok)
   delete(m, "name")     //刪除元素
  
fmt.Println(m)
   }

 

十二、字串

package main


func lengthSubstr(s string) int{
   last0curred := make(map[byte]int)
   start := 0
   maxlength := 0
   for i, ch := range []byte(s){
      if last0curred[ch] < start{
         start = last0curred[ch] + 1

      }
      if i - start + 1 > maxlength{
         maxlength = i - start +1
      }
      last0curred[ch] = i
   }
   return maxlength
}

func main() {
   lengthSubstr()
}
 
 
 
Strings.map等操作
 
 
 
 

十三、類

只是支援封裝,不支援繼承和多型。

 

 

沒有class 只有struct

 

Type point struct {i,j,int}

 

 

 

package main

import "fmt"

type
treeNode struct {
   value int
   left, right * treeNode
}

func createNode(value int) *treeNode{
   return &treeNode{value:value}
}

func main()  {
   var root treeNode
   root = treeNode{value:3}
   root.left = &treeNode{
   }
   root.right = &treeNode{5, nil, nil}
   root.right.left = new(treeNode)
   root.left.right = createNode(2)

   nodes := []treeNode{
      {value:3},
      {},
      {6, nil, &root},
   }
   fmt.Println(nodes)
}

 

結構建立在堆上還是棧上呢。

 

 

 

 

 

利用指標接收

 

 

 

十四、封裝

名字一般使用camelcase

 

首字母大寫public

 

首字母小寫 private

 

十五、擴充套件已有型別

 

包:每個目錄裡面一個包

Main包包含可執行入口

 

為結構定義的方法必須放在同一個包內

 

可以是不同的檔案。

 

Go語言沒有繼承:

 

如何擴充已有型別和別人的型別。

 

(1)定義別名

package queue

import "fmt"

type
Queue []int

func (q *Queue) Push(v int){
   *q = append(*q, v)
}

func (q *Queue) Pop() int {
   head := (*q)[0]
   *q = (*q)[1:]
   return head
}

func main(){
   q := queue.Queue{1}

   q.Push(2)
   q.Push(3)
   fmt.Println(q.Pop())
   fmt.Println(q.Pop())
   fmt.Println(q.IsEmpty())
   fmt.Println(q.Pop())
   fmt.Println(q.IsEmpty())
}

 

 

(2)使用組合

 

 

 

 

十六、Gopath環境

 

 

 

 

不用的包不能匯入,否則就會報錯的

 

 

 

 

 

 

 

十七、介面

1、duck typing

 

 

 

2、Go語言中的duck typing

 

假的retriever
package mock

type Retriever struct {      //定義retriever類
   Contents string
}

func (r Retriever) Get(url string) string{
   return r.Contents
}

 

 

really介面

package real

import (
   "net/http"
   "net/http/httputil"
   "time"
)

type Retriever struct {
   UserAgent string
   TimeOut time.Duration
}
func (r Retriever) Get(url string) string{
   resp, err := http.Get(url)
   if err != nil{
      panic(err)
   }
   result, err := httputil.DumpResponse(resp, true)
   resp.Body.Close()

   if err != nil{
      panic(err)
   }
   return string(result)
   }

 

 

 

 

package main

import ("fmt"
   "go-projects/retriever/mock"
)

type Retriever interface {     //定義了介面
   Get(url string) string     //定義了方法而已
}

func download(r Retriever) string {  //介面作為引數傳入,然後呼叫定義 的Get函式
   return r.Get("www.123.com")
}
func main()  {
   var r Retriever
   r = mock.Retriever{"this is imock"}
   fmt.Println(download(r))

}

3、介面定義:

 

介面是隱士的,只是實現裡面的方法啊而已

 

Interface  裡面有兩個東西一個是值,一個是型別,真實的值copyde,也可以是指標的。

 

 

 

4、介面的組合:

 

type RetrieverPoster interface {
   Retriever
   Poster
}

 

是許多小介面的組合,把許多小介面放在一起

 

 

傳值的時候傳retriever,都是具備的,r只是具備單一的

 

5、go語言標準的介面

 

Stringer:

 

Writer

 

十八、函數語言程式設計

 

函數語言程式設計  

 

函式指標   高階函式,函式到閉包

 

 

正統函數語言程式設計:

1、不可變性:不能有狀態,只有常量和函式

2、函式只能有一個引數。

 

package main

import "fmt"

func
adder() func(int) int{
   sum := 0
   return func(v int) int {
      sum += v
      return sum
   }
   }

func main() {
   a := adder()
   for i := 0; i < 10; i++{
      fmt.Println(a(i))
   }
}

 

 

 

 

正統式函式程式設計:

type iAdder func(int)(int, iAdder) 

func adder2(base int) iAdder {
   return func(v int) (int, iAdder) {
      return base + v, adder2(base + v)
   }
}
func main() {
a1 := adder2(0)
for i := 0; i < 10; i++{
   var s int
   s, a1 = a1(i)
   fmt.Printf("0 + 1 + ... + %d = %d\n", i, s)
}

}

 

Go語言中也有匿名函式,沒有名字的。

 

 

十九、異常處理

1、defer呼叫

package main

import "fmt"

func
tryDefer()  {
   defer fmt.Println(1)
   fmt.Println(2)
   fmt.Println(3)

}
func main() {
   tryDefer()
}

 

新增defer之後不受return 和panic的影響

Go語言是因為棧的,先進後出的。

 

 

實現斐波那契數列,必須注意的是函式名字的大寫

package fib

func Fibonacci() func() int{
   a, b := 0, 1
   return func() int {
      a, b = b, a + b
      return a
   }
}

 

 

package main

import (
   "bufio"
   "fmt"
   "os"
   "go-projects/functional/fib"
)

func tryDefer()  {
   defer fmt.Println(1)
   fmt.Println(2)
   fmt.Println(3)

}
func writeFile(filename string){
   file, err := os.Create(filename)
   if err != nil{
      panic(err)
   }
   defer file.Close()

   writer := bufio.NewWriter(file)
   defer writer.Flush()

   f := fib.Fibonacci()
   for i := 0; i<20; i ++{
      fmt.Fprintln(writer, f())
   }
}
func main() {
   tryDefer()
   writeFile("abc.txt")
}

 

 

何時呼叫 defer  

 

在open/close

 

Lock/unlock

 

Printhead/printfooter

 

 

錯誤處理的概念:

 

func writeFile(filename string){
   //file, err := os.Create(filename)
  
file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666)
   if err != nil{
      if pathError, ok := err.(*os.PathError); !ok{
         panic(err)
      }else {
         fmt.Println("%s, %s, %s\n",
            pathError.Op,
            pathError.Path,
            pathError.Err)
      }
      return
     
}
   defer file.Close()

   writer := bufio.NewWriter(file)
   defer writer.Flush()

   f := fib.Fibonacci()
   for i := 0; i<20; i ++{
      fmt.Fprintln(writer, f())
   }
}
func main() {
   tryDefer()
   writeFile("abc.txt")
}

 

自己建立error的型別

 

err = errors.New("this is a custom error")

 

 

 

 

 

 

統一的錯誤處理邏輯:

 

 

2、伺服器內部的錯誤資源處理流程,統一的處理

 

 

package main

import (
   "io/ioutil"
   "net/http"
   "os"
)

func main() {
   http.HandleFunc("/list/",
      func(writer http.ResponseWriter, request *http.Request) {
         path := request.URL.Path[len("/list/"):]
         file, err := os.Open(path)
         if err != nil{
            panic(err)
         }
         defer file.Close()
         all, err := ioutil.ReadAll(file)
         if err != nil{
            panic(err)
         }
         writer.Write(all)
         })
   err := http.ListenAndServe(":888", nil)
   if err != nil{
      panic(err)
   }
}

一個簡單的網頁 程式。

 

 

直接給前端頁面返回的是程式的錯誤程式碼

http.Error(writer,
   err.Error(),
http.StatusInternalServerError)

 

 

os.isempty   如果是空的話

 

 

 

使用主函式處理函式內部邏輯,外部進行一場的捕獲即處理例如Python的裝飾器的形式

 

func main() {
   http.HandleFunc("/list/",
      errWrapper(filelisting.Handlefilelist))

   err := http.ListenAndServe(":8888", nil)
   if err != nil{
      panic(err)
   }
}

 

 

 

panic和recover的區別:

 

1、panic

停止當前函式執行

一直向上返回,執行每一層的defer

如果沒有recover,程式退出。

 

2、recover

 

僅在defer呼叫中使用

獲取panic的值

如果無法獲取,可重新panic

 

 

工作流程如下:

panic(errors.New("this is an error"))自己可以建立的異常的值和型別。
 
package main

import (
   "errors"
   "fmt"
)

func tryRecover()  {
   defer func() {
      r := recover()
      if err, ok := r.(error); ok{
         fmt.Println("Error occurred", err)
      }else {
         panic(r)
      }
   }()
   panic(errors.New("this is an error"))
}
 
 
儘量少用panic。
 
意料之內的使用error, 檔案打不開
 
意料之外的使用panic, 陣列超屆
 
錯誤綜合處理方法: