Go資深工程師講解(慕課) 001

概覽

下載
開發: vi emacs idea eclipse vs sublime
Ide: GoLand,liteIDE
默認 gopath ~/go/src

基本語法

變量定義使用 var,函數外定義可以使用括號的方式

package main

import "fmt"

//函數外定義要使用var
var aa=3
var ss="kkk"
var bb =true

//可以使用括號的方式
var (
    cc int
    dd string
    ee bool
)

func variableZeroValue() {
    var a int
    var s string
    fmt.Println("%d %qn", a, s)
}

func variableInitialValue() {
    var a, b int = 3, 4
    var s string = "abc"
    fmt.Println(a, b, s)
}

func variableTypeDedtection() {
    var a, b, c, s = 2, 3, true, "def"
    //    類型推斷
    fmt.Println(a, b, c, s)
}

//冒號定義
func variableShorter() {
    //第一次
    a, b, c, s := 2, 3, true, "def"
    //    類型推斷
    fmt.Println(a, b, c, s)
}

func main()  {
    fmt.Println("hello world")
    variableZeroValue()
    variableInitialValue()
    variableTypeDedtection()
    variableShorter()

}

內建類型

  • bool string
  • (u)int (u)int8 (u)int16 (u)int32 (u)int64 uintptr(指針)
  • byte,rune(字符 go 的 char 類型,32 位)
  • float32,float64,complex64,complex128(complex 複數類型,實部虛部)

複數回顧

  • i = $sqrt{-1}$
  • 複數:3+4i
  • $|x+y|$ 的模 = $sqrt{3^2+4^2}$ = 5
  • $i^2$=-1,$i^3$=-i,$i^4$=1,...
  • $e^{iphi}$ = cos$phi$+isin$phi$

e 是單位圓,$phi$是逆時針旋轉的角度,如圖 (泰勒極數展開)

pie.png

  • $e^{iphi}$ = $sqrt{cos^2phi+sin^2phi}$ = 1
  • $e^{ipi}$ = -1, $e^{ifrac{3}{2}pi}$ = -i,$e^{i2pi}$=1
  • 推導出歐拉公式 $e^{ipi}$+1=0

go 語法來驗證

func euler(){
    c:= 3+4i //表示複數
    fmt.Println(cmplx.Abs(c))
    //歐拉公式
    fmt.Println(cmplx.Pow(math.E,1i*math.Pi)+1)
    fmt.Println(cmplx.Exp(1i*math.Pi)+1)
}

強制類型轉換

go 只有強制類型轉換

func triangle() {
    var a, b int = 3, 4
    var c int
    c = int(math.Sqrt(float64(a*a + b*b)))
    fmt.Println(c)
}

常量定義

func consts() {
    const filename = "abc.txt"
    const a, b = 3, 4
    var c int
    c = int(math.Sqrt(float64(a*a + b*b)))
    fmt.Println(filename, c)
}

枚舉類型

  • 普通枚舉類型
  • 自增值枚舉類型
func enums() {
    const (
        cpp    = 0 //iota const是自增值
        java   = 1
        python = 2
        gloang = 3
    )
    //    b,kb,mb,gb,tb,pb
    const (
        b = 1 << (10 * iota)
        kb
        mb
        gb
        tb
        pb
    )
}

回顧

  • 變量類型寫在變量之後
  • 編譯器可推測變量類型
  • 沒有 char,只有 rune
  • 原生支持複數類型

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.Printf("%sn",contents)
    }
}

// 或者條件之前先賦個值用分號分隔
func main() {
    const filename = "abc.txt"
    //contents,err:=ioutil.ReadFile(filename)
    if contents,err:=ioutil.ReadFile(filename); err!=nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%sn",contents)
    }
}

switch

// switch 沒有break;

func switchFunc(a, b int, op string) int {
    var result int
    switch op {
    case "+":
        result = a + b
    case "-":
        result = a - b
    case "*":
        result = a * b
    case "/":
        if b == 0 {
            fmt.Printf("%sn", errors.New("除數為零"))
            break
        }
        result = a / b
    default:
         fmt.Printf("not found operation")
    }
    return result
}
// 另外一種switch不寫值,而在case中寫條件判斷

func grade(score int) string {
    g := ""
    switch {
    case score<0 || score>100
        panic(fmt.Sprintf("Wrong score: %d", score)) //中斷程序執行
    case score < 60:
        g = "F"
    case score < 80:
        g = "C"
    case score < 90:
        g = "B"
    case score <= 100:
        g = "A"
    }
    return g
}

switch 會自動 break,除非使用 fallthrough

for

直觀的是沒有括號

package main

import (
    "fmt"
    "strconv"
)

func convertToBin(n int) string {
    result := ""
    for ; n > 0; n /= 2 {
        lsb := n % 2
        result = strconv.Itoa(lsb) + result
    }
    return result
}

func main() {
    fmt.Println(
        convertToBin(5),  //101
        convertToBin(13), //1011--> 1101
    )
    // 調用時,返回值不有可以用_來點位
}

遞增條件也可以省略

func printFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        panic(err)
    }
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
       //相當於while
       fmt.Println(scanner.Text())
    }
}
//甚麼都不加就是死循環,因為併發編程時要用到gorutine

函數

函數名在前 返回值在後。返回值可以是多個值,可以給返回值取名字;通常情況下返回一個值,一個 error,函數的參數也可以是個函數,它沒有默認參數,有一個可變參數列表...

package main

import (
    "fmt"
    "math"
    "reflect"
    "runtime"
)

func main() {
    //div(13, 3)
    fmt.Println(apply(pow, 2, 3))
    fmt.Println(apply(func(i int, i2 int) int {
        return int(math.Pow(float64(i), float64(i2)))
    }, 3, 4))
    fmt.Println("多參數和:", sum(1, 2, 3))
}
func divid(a,b int) (int,int){
    var q,r int
    q = a/b
    r = a%b
    return q,r
}
// 多個返回值,並指定名字
func div(a, b int) (q, r int) {
    q = a / b
    r = a % b
    return
}

func pow(a, b int) int {
    return int(math.Pow(float64(a), float64(b)))
}
// 參數傳函數
func apply(op func(int, int) int, a, b int) int {
    p := reflect.ValueOf(op).Pointer()
    opName := runtime.FuncForPC(p).Name()
    fmt.Printf("Calling function %s with args"+
        "(%d,%d)n", opName, a, b)
    return op(a, b) //也可以使用匿名函數
}
// 可變參數
func sum(number ...int) int {
    sum := 0
    for _, v := range number {
        sum += v
        //fmt.Println(v)
    }
    return sum
}

特點

  • 返回值類型寫在最後面
  • 可返回多個值
  • 函數可作為參數
  • 沒有默認參數,可變參數列表

指針

var a int=2
var pa *int = &a
*pa = 3
// 有指針,但指針不能運算

go 傳值,引用傳遞? go 語言只有值傳遞一種方式

// 沒效果
func main(){
    c, d := 2, 3
    fmt.Printf("c=%v,d=%vn", c, d)
    swap(c, d)
    fmt.Printf("nswaped c=%v,d=%vn", c, d)
}
// 值傳遞的,還是地址傳遞的說明
func swap(a, b int) {
    a, b = b, a
}
// 解決一 swap參數為地址,調用時要傳地址
func main(){
    c, d := 2, 3
    fmt.Printf("c=%v,d=%vn", c, d)
    swap(&c, &d)
    fmt.Printf("nswaped c=%v,d=%vn", c, d)
}
// 值傳遞的,還是地址傳遞的說明
func swap(a, b *int) {
    *a, *b = *b, *a //地址交換
}
// 解決二,返回新的數據
func swapv1(a,b int) (int,int){
    return b,a
}

數組

func main() {
    var arr1 [5]int
    arr2:=[3]int{1,3,5}
    arr3:=[...]int{2,4,6,8,10} //不寫...就是切片了
    fmt.Println(arr1,arr2,arr3)
    // 遍歷數據
    for i := 0; i < len(arr3); i++ {
        fmt.Println(arr3[i])
    }
    //    一般使用range來遍歷 i是下標,v是值
    for i, v := range arr3 {
        fmt.Println(i, v)
    }
    // 最大值
    maxi, maxv := MaxNumber(&arr3)
    fmt.Printf("nMax index %v value %vn",maxi, maxv)

    arr4:=[]int{1,3,5,7,9,11}
    maxi, maxv := MaxNumberv1(&arr4)
    fmt.Printf("nMax index %v value %vn",maxi, maxv)
}


// 求最大值
func MaxNumber(numbers *[5]int)(int,int){
    maxi:=-1
    maxValue:=-1
    for i,v:=range numbers {
        if v>maxValue {
            maxi,maxValue = i,v
        }
    }
}

// 求最大值
func MaxNumberv1(numbers *[]int) (int, int) {
    maxi := -1
    maxValue := -1
    for i, v := range *numbers {
        if v > maxValue {
            maxi, maxValue = i, v
        }
    }
    return maxi, maxValue
}

數組是值類型 而且必須規定長度

func printArray(arr [5]int) {
    // 注意這個參數 [5]int 和 []int不是一回事兒
    // 也可以*[5]int也可以傳地址
    for i,v:=range arr {
        fmt.Println(i,v)
    }
}

// main中調用時
printArray(arr3) //對的
printArray(arr4) //會報類型錯誤
printArray(arr1) //對的
printArray(arr2) //錯的 因為長度對不上,認為是兩種類型

切片 Slice

理解為它是數組的一個視圖

package main

import "fmt"

func main() {
    arr := [...]int{1, 2, 3, 4, 5, 6, 7}
    s := arr[2:6]
    fmt.Println("arr=", arr)
    fmt.Println("arr[2:6]=", s)
    fmt.Println("arr[2:]=", arr[2:])
    fmt.Println("arr[:6]=", arr[:6])
    fmt.Println("arr[:]=", arr[:])
    s1 := arr[2:]
    s2 := arr[:]
    fmt.Println("s1=", s1)
    fmt.Println("s2=", s2)
    fmt.Println("After updateSlice(s1)")
    UpdateSlices(s1)
    fmt.Println(s1)
    fmt.Println(arr)

    fmt.Println("After updateSlice(s2)")
    UpdateSlices(s2)
    fmt.Println(s2)
    fmt.Println(arr)
}

func UpdateSlices(s []int) {
    s[0] = 100
}


/*
arr= [1 2 3 4 5 6 7]
arr[2:6]= [3 4 5 6]
arr[2:]= [3 4 5 6 7]
arr[:6]= [1 2 3 4 5 6]
arr[:]= [1 2 3 4 5 6 7]
s1= [3 4 5 6 7]
s2= [1 2 3 4 5 6 7]
After updateSlice(s1)
[100 4 5 6 7]
[1 2 100 4 5 6 7]
After updateSlice(s2)
[100 2 100 4 5 6 7]
[100 2 100 4 5 6 7]
*/

證明 slice 是數組的一個視圖,

reslice

    fmt.Println("Reslice:")
    s2=s2[:5]
    s2=s2[2:]
    fmt.Println("Reslice s2[:5] s2[2:]",s2)
    s2[0] = 200
    fmt.Println("arr", arr)
    /*
    Reslice:
    Reslice s2[:5] s2[2:] [100 4 5]
    arr [100 2 200 4 5 6 7]
    它們都是不同的view array
    */
    arr[0], arr[2] = 1, 2
    s1 = arr[2:6]
    s2 = s1[3:5]
    fmt.Println("s1=", s1)
    fmt.Println("s2=", s2)
/*
rr [100 2 200 4 5 6 7]
s1= [2 4 5 6]
s2= [6 7]
*/

為甚麼能取出這樣的值呢看下圖,slice 下標更新示意圖

slice

只要你不超過 capicity

slice_capicity

slice 可以向後擴展,不可以向前擴展,s[i]不可以超越len(s),向後擴展不可以超越底層數組cap(s)

    arr[0], arr[2] = 1, 2
    s1 = arr[2:6]
    s2 = s1[3:5]
    fmt.Printf("s1=%d,len(s1)=%d,cap(s1)=%vn", s1, len(s1), cap(s1))
    fmt.Printf("s2=%d,len(s2)=%d,cap(s2)=%vn", s2, len(s2), cap(s2))

向 slice 中不斷的添加值

    s3 := append(s2, 10)
    s4 := append(s3, 11)
    s5 := append(s4, 12)
    fmt.Println("arr", arr)
    fmt.Println("s3,s4,s5", s3, s4, s5)

s4,s5 就不是原來的 arr,系統將會分配更新長的的數組。

添加元素時如果超越 cap,系統會重新分配更大的底層數組,原來數組有人用就保留,沒有人用就回收

package main

import "fmt"

func main() {
    var s []int //zero value for slice is nil

    for i := 0; i < 100; i++ {
        printSlice(s)
        s = append(s, 2*i+1)
    }
    fmt.Println(s)
    s1:=[]int{2,4,6,8}
    // 長度是16
    s2:=make([]int,16)
    s3:=make([]int,10,32)
}

func printSlice(s []int) {
    fmt.Printf("len=%d,cap=%dn", len(s), cap(s))
}
// 它每次都是乘以2

聲明 slice

s1:=[]int{2,4,6,8}
    // 長度是16
    s2:=make([]int,16)
    s3:=make([]int,10,32)
    fmt.Printf("s1=%v",s1)
    fmt.Printf("s2=%v",s2)
    fmt.Printf("s3=%v",s3)

copy(s2,s1)

    copy(s2, s1)
    fmt.Printf("copy(s2,s1)s2=%vn", s2)
    printSlice(s2)

刪除 slice delete

    fmt.Println("Deleting elements from slice")
    s2 = append(s2[:3], s2[4:]...)
    fmt.Printf("copy(s2,s1)=>s2=%vn", s2)
    printSlice(s2)

從頭或尾刪除

    // pop tail
    fmt.Println("Popping from front")
    front := s2[0]
    s2 = s2[1:]
    fmt.Println("front=", front)
    printSlice(s2)

    fmt.Println("Popping from back")
    tail := s2[len(s2)-1]
    s2 = s2[:len(s2)-1]
    fmt.Println(tail)
    printSlice(s2)

Map

定義 map[k]v map[k1]map[k2]v 後面可以跟大括號初始化值

    m := map[string]string{
        "name":    "ccmouse",
        "course":  "golang",
        "site":    "imooc",
        "quality": "notabd",
    }
    fmt.Println(m)
    m2 := make(map[string]int) // m2== empty map
    var m3 map[string]int      // m3==nil
    fmt.Println(m, m2, m3)

    //    遍歷
    for k, v := range m {
        fmt.Println(k, v) //這是無序的
    }
    // 獲取名字
    fmt.Println("Getting values")
    courseName := m["course"]
    fmt.Println(courseName)
    //    如果不存在它會取zero value 本例中就是空串
    if courseName, ok := m["cs"]; ok {
        fmt.Println(courseName)
    } else {
        fmt.Println("key does not exist")
    }
    //刪除元素
    fmt.Println("Deleteing values")
    name, ok := m["name"]
    fmt.Printf("Getting key='name' value=%v is %vn", name, ok)
    fmt.Println("Deleting...")
    delete(m, "name")
    name, ok = m["name"]
    fmt.Printf("Getting key='name' value=%v is %vn", name, ok)
  • 創建make(map[string]int)
  • 獲取m[key]
  • key 不存在獲取的 value 類型的初始值(0 值)
  • value,ok:=m[key]來判斷是否存在 key
  • delete()刪除
  • range 遍歷,它是無序的,要排序要把 key 放在 slice 中排序再輸出
  • map 使用哈希表,必須可以比較相等
  • 除了 slice,map,function 的內建類型都可以作為 key
  • Struct 類型不包含上述字段,可以作為 key

實例

尋找最長不含有重復字符的子字符串 abcabcbb-->abc
bbbbb->b
pwwkew->wke

對於每一個字母 X

  • lastOccurred[x]不存在,或者< start 無需操作
  • lastOccurred[x]>=start更新 start 將 start 更新到 x+1 的位置
  • 更新lastOccurred[x],更新 maxLength
package main

import "fmt"

func nonRep(s string) int {
    lastOccurred := make(map[byte]int) //將byte=>rune(支持中文)
    start := 0
    maxLength := 0
    for i, ch := range []byte(s) { //將byte=>rune (支持中文)
        if lastI, ok := lastOccurred[ch]; ok && lastI >= start {
            start = lastOccurred[ch] + 1
        }
        if i-start+1 > maxLength {
            maxLength = i - start + 1
        }
        lastOccurred[ch] = i
    }
    return maxLength
}

func main()  {
    fmt.Println(nonRep("abcabcbb"))
}

上例支持中文 將byte=>rune(支持中文)

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/6729

(0)
Walker的頭像Walker
上一篇 11小時前
下一篇 9小時前

相關推薦

  • Go工程師體系課 006

    項目結構說明:user-web 模塊 user-web 是 joyshop_api 工程中的用戶服務 Web 層模塊,負責處理用戶相關的 HTTP 請求、參數校驗、業務路由以及調用後端接口等功能。以下是目錄結構說明: user-web/ ├── api/ # 控制器層,定義業務接口處理邏輯 ├── config/ # 配置模塊,包含系統配置結構體及讀取邏輯 …

    後端開發 8小時前
    000
  • Go資深工程師講解(慕課) 008_GMP調度器與Go設計哲學

    Go GMP 調度器與設計哲學 對應視頻 9-2 go語言的調度器、18-1 體會Go語言的設計、18-2 課程總結 1. Go 調度器演進 1.0 時代:單線程調度器(Go 0.x) 只有一個線程運行 goroutine 所有 goroutine 排隊等待 無法利用多核 1.1 時代:多線程調度器(Go 1.0) 引入多線程 但全局鎖競爭嚴重,性能瓶頸 1…

  • 編程基礎 0009_testing詳解

    Go testing 詳解 目錄 testing 包基礎 表格驅動測試 子測試 t.Run 基準測試 Benchmark 測試覆蓋率 TestMain httptest 包 Mock 和接口測試技巧 模糊測試 Fuzz 1. testing 包基礎 1.1 測試文件和函數命名規則 Go 測試遵循嚴格的命名約定: 測試文件以 _test.go 結尾(如 use…

    後端開發 18小時前
    000
  • Go工程師體系課 005

    微服務開發 創建一個微服務項目,所有的項目微服務都在這個項目中進行,創建joyshop_srv,我們無創建用戶登錄註冊服務,所以我們在項目目錄下再創建一個目錄user_srv 及user_srv/global(全局的對象新建和初始化)user_srv/handler(業務邏輯代碼)user_srv/model(用戶相關的 model)user_srv/pro…

    後端開發 9小時前
    000
  • Go工程師體系課 009

    其它一些功能 個人中心 收藏 管理收貨地址(增刪改查) 留言 拷貝inventory_srv--> userop_srv 查詢替換所有的inventory Elasticsearch 深度解析文檔 1. 甚麼是Elasticsearch Elasticsearch是一個基於Apache Lucene構建的分布式、RESTful搜索和分析引擎,能夠快速地…

    後端開發 5小時前
    000
簡體中文 繁體中文 English