天天看點

Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

一 運算符

1 運算符基礎

1 算法: 解決問題的過程,運算符和表達式來串聯資料和指令。

算數運算符

指派運算符

比較運算符

邏輯運算符

位運算符

其他相關運算符

2 算數運算符

1 簡介

算術運算符是對數值類型的變量進行運算的,如加減乘除,在Go語言中使用較多
運算符 描述
+ 相加
- 相減
* 相乘
/ 相除
% 求餘
++ 自增
-- 自減

2 重點講解

除(/)和取模(%)

針對除(/): 預設的當除号兩邊都是整數時,其值為整數,其會直接削減掉對應的小數部分,當一個為小數時,則會繼承小數的屬性

針對取模(%) a%b=a-a/b*b 及 -10%3=-10-(-10/3)*3=-10-(-9)=-1

自增和自減 ++ --

Go語言中自增自減隻能當成獨立語言使用,不能是b:=a++ 或 b:=a--

Go 語言中的++ 和 -- 隻能在變量後面,不能在變量前面,及沒有++a和--a

Go 語言中自增,自減不是運算符,隻能作為獨立語句,不能用于表達式

3 關系運算符

關系運算符的結果都是bool類型,也就是要麼是true,要麼是false

關系表達式經常用在if結構條件判斷中或循環結構中使用

== 檢查兩個值是否相等,如果相等則傳回True,否則傳回False
!= 檢查兩個值是否不相等,如果不相等傳回True,否則傳回False
> 檢查左邊值是否大于右邊值,如果是傳回 True 否則傳回 False
< 檢查左邊值是否小于右邊值,如果是傳回 True 否則傳回 False
>= 檢查左邊值是否大于等于右邊值,如果是傳回 True 否則傳回 False
<= 檢查左邊值是否小于等于右邊值,如果是傳回 True 否則傳回 False

2 細節說明

1 關系運算符的結果都是bool類型,也就是要麼是true,要麼是false

2 關系運算符組成的表達式,稱為關系表達式

3 比較運算符的== 不能寫成=

4 邏輯運算符

用于連結多個條件,最終結果是一個bool
&& 邏輯 AND 運算符。 如果兩邊的操作數都是 True,則條件 True,否則為 False
邏輯或 運算符。 有一個是True,則條件 True,否則為 False
! 邏輯 NOT 運算符。 如果條件為 True,則邏輯 NOT 條件 False,否則為 True

2 注意事項

&& 也叫短路與,a && b ,及若a為false,則結果必為false

|| 也叫短路或,a || b, 若a為true,則結果必為true

5 指派運算符

1 介紹

指派運算符就是将某個運算運作後指派給指定的變量
= 簡單的指派運算符,将一個表達式的值賦給一個左值
+= 相加後再指派
-= 相減後再指派
*= 相乘後再指派
/= 相除後再指派
%= 求餘後再指派
<<= 左移後指派
右移後指派
&= 按位與後指派
^= 按位異或後指派
按位或後指派
通過相關的指派語句完成對應的操作

2 指派運算特點

1 運算順序從右向左

2 指派運算符的左邊隻能是變量,右邊可以是變量,常量,表達式

3 複合指派運算等價于下面結果

如 a+=3等價于 a=a+3

6 位運算符

1 源碼,反碼,補碼

二進制是逢2進位的進位制,0,1 是基本算符

現代的電子計算機技術全部采用的是二進制,因為它隻使用0,1兩個數字元号,非常友善,易使用電子方式實作,計算機内部處理的資訊,都是采用二進制數表示的,二進制數用0或者1可表示任何數,進位規則是"逢2進1",數字1在不同的位上表示不用的值,按從右至左的次序,這個值是以二倍遞增

在計算機内部,運算各種運算,就是以二進制的方式運作的

源碼: 及原來資料的二進制表示

反碼:符号位不變,其他位相反

補碼:整體加1

正數的源碼,反碼和補碼都一樣

負數的反碼=原碼的符号位不變,其他位取反

負數的補碼==他的反碼+1

0的反碼,補碼都是0

在計算機運算的時候,都是以補碼的方式來進行運算的

如下

-9 二進制源碼表示為: 1000 1001

則二進制反碼為: 1111 0110

則二進制補碼為: 1111 0111

package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    fmt.Printf("輸出結果為%d      %d", -5>>2, -15>>2)
    /*
        分析過程
        -5>>2
        結果分析如下

        源碼
        1000  0110
        反碼
        1111  1001
        補碼
        1111  1010

        右移兩位
        補碼
        1111  1110
        反碼
        1111   1101
        源碼
        1000   0010
        及 -2

        -14 >>2
        源碼1000 1110
        反碼 1111 0001
        補碼 1111 0010
        右移兩位
        補碼  1111  1100
        反碼  1111  1011
        源碼  1000  0100  及  -4
    */
}
           
結果如下
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 位運算符

& 按位與運算符"&"是雙目運算符。 其功能是參與運算的兩數各對應的二進位相與
按位或運算符" "是雙目運算符。 其功能是參與運算的兩數各對應的二進位相或
^ 按位異或運算符"^"是雙目運算符。 其功能是參與運算的兩數各對應的二進位相異或,當兩對應的二進位相異時,結果為1
<< 左移運算符"<<"是雙目運算符。左移n位就是乘以2的n次方。 其功能把"<<"左邊的運算數的各二進位全部左移若幹位,由"<<"右邊的數指定移動的位數,高位丢棄,低位補0
右移運算符">>"是雙目運算符。右移n位就是除以2的n次方。 其功能是把">>"左邊的運算數的各二進位全部右移若幹位,">>"右邊的數指定移動的位數。

7 其他運算符

& 傳回變量存儲位址

* 指針變量,傳回其指針對應的變量的值

Go 語言不支援三元運算符

8 進制介紹

對于整數有4中表達方式

1 二進制, 0,1 滿2進1

2 十進制 0-9,滿 10 進1

3 八進制 0-7 滿八進一 以數字 0開頭表示

4 十六進制 0-9 及 A-F,滿16 進1 ,以0x或0X開頭,此處A-F不區分大小寫

2 進制轉換

1 其他進制轉十進制

二進制轉換為十進制

規則: 從最低位開始(右邊的),将每個為上的數字提取出來,乘以2的(位數-1)次方

然後求和

八進制轉10進制

規則:從最低位(右邊的),将每個為上的數字提取,乘以8的(位數-1)次方

十六進制轉十進制

規則:從最低位開始,将每個位上的數提取出來,乘以16的(位數-1)次方,然後求和

2 十進制轉其他進制

十進制轉2進制

規則:将該數不斷除以2,直到商為0為止,然後将每步得到的餘數倒過來,就是對應的二進制

十進制轉八進制

将該數不斷除以8,直到商為0即可,然後将每步得到的餘數倒過來,就是對應的八進制

十進制轉16進制

規則: 将該數不斷除以16,直到商為0,然後将每步得到的餘數倒過來,就是對飲的十六進制

3 二進制轉其他進制

二進制轉八進制

規則:三位一組進行處理和轉換

二進制轉十六進制

規則: 将二進制數每四位一組,組合成對應的十六進制數即可

4 其他進制轉二進制

八進制轉二進制

規則: 将八進制數每1位,轉成對應的三位的二進制數即可

十六進制轉二進制

規則: 将十六進制的每一位數,轉換成4位的二進制數即可

9 運算符優先級

一進制算法運算符優先級最高,二進制則分成五個級别,從高往低分别是
優先級
5 * / % << >> & &^
4 + - ^
3 == != < <= > >=
2
1
相同優先級的二進制運算符,從左往右依次計算

10 鍵盤輸入語句

1 單次輸入

在程式設計中,需要接受使用者輸入的資料,可使用鍵盤輸入語句來進行擷取相關參數的操作
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    var name string
    fmt.Println("請輸入")
    fmt.Scanln(&name)
    fmt.Println("name=", name)
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 同時輸入多個

package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    var name string
    var age int
    fmt.Println("請輸入")
    fmt.Scanf("%s  %d", &name, &age)
    fmt.Printf("name=%s,age=%d", name, age)

}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

二 控制語句

在程式中,程式運作的流程控制決定程式如何執行,是必須掌握的,主要有三大流控語句

1 順序控制

2 分支控制

3 循環控制

2 順序控制

程式從上到下逐漸執行,中間沒有任何判斷和跳轉

3 分支控制

1 if 控制

1 單分支

單分支

基本文法

if 條件表達式 {

執行語句塊

}

當表達式為true,會執行其中的語句塊的内容

細節說明: go 的if有一個強大的就是條件判斷裡面允許聲明一個變量,這個變量的作用域隻能在該條件的邏輯塊内,在其他地方不起作用

示例如下
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    if i := 20; i > 19 {
        fmt.Println(i)
    }
    fmt.Println(i)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 雙分支

判斷流程:

先判斷條件表達式是否成立,若為真,則執行代碼塊1

若為假,則執行代碼塊2

3 多分支

判斷流程:

先判斷條件表達式1是否成立,若不成立,則執行下一個,若成立,則執行, 若都不成立,則執行else

4 嵌套分支

在一個分支結構中又完成的嵌套了另一個完成的分支結構,裡面的分支的結構稱為内層分支,外部的分支稱為外層分支

建議嵌套分支不宜太多,建議控制在3層

2 switch

switch 語句用于基于不同條件執行不同動作的情況,每個case分支都是唯一的,從上到下逐一比對,直到比對為止
switch 表達式 {
            case  表達式1,表達式2,...:  //表達式之間是或的關系
                            語句塊
            case  表達式3,表達式4...:
                            語句塊
            // 此處有多個表達式 
            default:
                            語句塊
    }           

2 switch 細節

1 case 後面是一個表達式

2 case 後各個表達式的值的資料類型必須和switch的表達式資料類型一緻

3 case 後面可以帶多個表達式,使用逗号隔開,比如case 表達式1,表達式2

4 case 後面的表達式如果是常量值(字面值),則要求不能重複

5 case 後面不需要break,程式比對到一個case就會執行對應的代碼塊,然後退出switch,如果一個都比對不到,則執行default

6 default 不是必須的

7 switch後面也可以不帶表達式,類似if-else分支來使用

8 switch後面也可以直接聲明/定義一個變量,分号結束,不推薦

9 switch穿透fallthrough,如果在case語句塊增加了fallthrough,則會繼續執行下一個case,也叫switch穿透

10 type switch: switch語句還可以被用于type-switch來判斷一個interface變量中實際指向的變量

3 實戰

1 判斷輸入資料類型
package main

import "fmt"

func Test(x interface{}) { // 此處使用interface類型,其若為空,則表示可是任何類型,後面可說明
    switch x.(type) { // 此處用于判斷輸入值的類型
    case nil:
        fmt.Printf("this  type is  %T\n", x)
    case float64:
        fmt.Printf("this  type is  %T\n", x)
    case string:
        fmt.Printf("this  type is  %T\n", x)
    case int:
        fmt.Printf("this type is %T\n", x)
    default:
        fmt.Println("未知資料類型")
    }
}
func main() { //定義main函數,一般的檔案執行的入口函數都是main
    x := 10
    y := 20.1
    z := "abcd"
    Test(x)
    Test(y)
    Test(z)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器
2 穿透相關
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    switch { //switch預設是true
    case false:
        fmt.Println("The integer was <= 4")
        fallthrough
    case true:
        fmt.Println("The integer was <= 5")
        fallthrough
    case false: //此處不會進行判斷,直接進入此處
        fmt.Println("The integer was <= 6")
        fallthrough
    case true:
        fmt.Println("The integer was <= 7")
        fallthrough
    case false:
        fmt.Println("The integer was <= 8")
    default:
        fmt.Println("default case")
    }
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

4 循環控制

1 基本文法

for  循環變量初始化;循環條件;循環變量疊代{
                循環操作(語句)
}           
文法格式說明
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    for i := 1; i < 10; i++ {
        fmt.Println(i)
    }
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 for 循環執行步驟

循環變量初始化

循環條件

循環操作

循環變量疊代

3 注意事項和細節說明

循環條件是傳回一個布爾表達式

4 for 循環的第二種使用方式

for  循環條件 {
            循環執行語句
}           

将變量初始化和變量疊代寫到其他位置

package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    i := 1
    for i < 10 {
        fmt.Println(i)
        i++;
    }
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

5 for 循環的第三種方式

for   {
                        // 循環執行語句
        }           
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    i := 1
    for {
        if i == 10 {
            break
        } else {
            fmt.Println(i)
        }
        i++;
    }
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 for 循環周遊

1 for...range 周遊

package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    var str string
    fmt.Scanf("%s", &str)
    for k, v := range str {
        fmt.Printf("index=%d ,value=%c\n", k, v)
    }
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器
注意: for-range 在周遊字元串時,是按照字元來周遊的,而不是for循環預設的使用位元組進行周遊

3 多重循環控制

1 将一個循環放在另一個循環中,就形成了嵌套循環,在外層的被稱為外層循環,在内部稱為内層循環

2 一般情況下,當内層循環的判斷條件為false時,才會跳出内層循環

3 設外層循環次數為m,内層循環次數為n,則内層循環一般要執行m*n此

2 基本執行個體,列印乘法口訣

package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    for i := 1; i < 10; i++ {
        for j := 1; j <= i; j++ {
            fmt.Printf("%d*%d=%d    ", j, i, i*j)
        }
        fmt.Println()
    }
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

4 for 循環的break 和 continue

break 跳出本循環體,continue 跳過本次循環
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    for j := 1; j <= 2; j++ {
        for i := 1; i < 5; i++ {
            fmt.Println("内層", i)
            if i == 2 {
                break //跳出此輪循環,而不是直接跳出所有循環,隻是某一個for
            }
        }
        fmt.Println("外層", j)
    }
    fmt.Println("continue----------------")
    for i := 1; i < 5; i++ {
        if i == 2 {
            continue // 跳出此次循環
        }
        fmt.Println(i)
    }
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

5 label 使用

用于跳轉到指定位置
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
lable2:
    for j := 1; j <= 2; j++ {
        for i := 1; i < 5; i++ {
            fmt.Println("内層", i)
            if i == 2 {
                break lable2 //加上标簽,表示跳出至标簽位置
            }
        }
        fmt.Println("外層", j)
    }
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

6 跳轉到指定代碼标簽(goto)

goto語句通過标簽進行代碼間的無條件跳轉,goto語句可以在快速跳出循環、避免重複退出上有一定的幫助,Go語言使用goto語句能簡化一些代碼的實作過程。
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    var breakAgain bool
    //外層循環
    for x := 0; x < 10; x++ {
        fmt.Println("外層循環", x)
        //内層循環
        for y := 0; y < 10; y++ {
            //滿足條件則退出
            fmt.Println("内層循環", y)
            if y == 2 {
                breakAgain = true
            }
        }
        if breakAgain { //外層循環處理退出機制
            break
        }
    }
    fmt.Println("done") // 退出最終機制
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 goto 處理問題

package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    //外層循環
    for x := 0; x < 10; x++ {
        fmt.Println("外層循環", x)
        //内層循環
        for y := 0; y < 10; y++ {
            //滿足條件則退出
            fmt.Println("内層循環", y)
            if y == 2 {
                //跳轉到标簽。直接退出
                goto breakHere
            }
        }
    }
    //return
breakHere:
    fmt.Println("done")
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

7 return

使用在方法或函數中,表示跳出所在的方法或函數

說明

1 如果return 在普通函數中,表示跳出該函數,則不在執行return後面的代碼,也可以了解為終止該函數

2 若return在main函數,表示終止main函數,也就是終止程式

三 函數和包

1 函數

1 概述

函數是組織好的,可重複使用的,用來實作單一或相關功能的代碼段,其可以提高應用的子產品性和代碼的重複使用率,其是完成某一功能的程式指令的集合,其分為自定義函數和系統函數

2 特點

1 減少代碼備援

2 提升代碼可維護性

3 聲明函數

普通函數需要先聲明才能調用,一個函數的聲明包括參數和函數名等,編譯通過了才能了解函數應該怎樣調用代碼和函數體之間傳遞參數和傳回函數。

1 普通函數的聲明形式

Go 語言的函數聲明以func辨別,後面緊跟着函數名、參數清單、傳回參數清單及函數體,具體形式如下
func  函數名(形參清單)   (傳回值清單)  {
    執行語句
        return  傳回值清單 
}           
函數名:由字母,數字,下劃線組成,其中,函數名的第一個字母不能為數字,在同一個包内,函數名稱不能重名。
形參清單: 表示函數的輸入 ,其由參數變量和參數類型組成,其中,參數清單中的變量作為函數的局部變量而存在。
執行語句:表示為了實作某一功能的代碼塊。

傳回值:其可以有傳回值,也可以沒有,其有傳回值時,必須在函數體中使用return語句提供傳回值清單。

函數體:能夠被重複調用的代碼片段。

2 參數類型簡寫

func  add(a,b int) int  {
    return  a+b
}           
若有多個參數變量,以逗号進行分割,若相鄰變量是同類型,則可将寫成一個

3 函數的傳回值

Go 語言支援多傳回值,多傳回值能友善擷取函數執行後的多個傳回參數,Go語言經常使用多傳回值中的最後一個傳回參數傳回函數執行中可能發生的錯誤
1 純類型的傳回值
func Test(a, b int) (int, int) {
    return a + b, a * b
}
           
2 帶有變量名的傳回值類型
Go 支援對傳回值類型進行命名,這樣傳回值就和參數一樣擁有變量名和類型
package main

import "fmt"

func Test(a, b int) (x, y int) {  //此處通常用于辨別傳回值情況
    return a + b, a * b
}

func main() {
    m, n := Test(10, 20)
    fmt.Println(m, n)
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

4 函數調用機制

Go 支援函數傳回多個值

1 若傳回多個值,在接受時,希望忽略某個傳回值,則可以使用_符号表示占位符

2 如果傳回值隻有一個,則傳回值類型清單可以不寫括号

3 為了讓其他包的檔案,可以通路到本包的函數,則該函數名的首字母需要大寫,類似其他語言的公共參數,這樣才能挎包通路

4 在通路其他函數時,其文法是 包名.函數名

5 如果包名稱比較長,則Go支援使用别名的方式進行處理

6 在同一個包下,不能有相同的函數名,否則重複定義

7 如果要編譯成一個可執行程式檔案,就需要這個包聲明為main,及package main,這就是一個文法規範,如果是寫一個庫,則包名可以自定義。

package main

import "fmt"

func Test(i int) int { //函數定義,當傳回是為一個時,其不用寫括号
    i += 1
    return i
}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    i := 10
    j := Test(i) // 函數調用
    fmt.Println(j)
    fmt.Println(i) //列印原值。因為函數調用會形成新的空間,其會進行值拷貝,是以其不會影響原來main函數中對應的值
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

5 函數的注意事項和細節讨論

1 函數的形參清單可以是多個,傳回值也可以是多個

2 形參清單和傳回值的資料類型可以是值類型和引用類型

3 函數的命名遵循辨別符命名規範,首字母不能是數字,首字母大寫該函數可以被本包和其他封包件引入調用,,首字母小寫,隻能被本包使用,其他封包件不能使用

4 函數中變量是局部變量,函數外不生效

5 基本資料類型和數組預設都是值傳遞,進行值拷貝,在函數内修改,不會影響到函數外原來的值

6 如果希望函數内的變量能夠改變函數外的變量,可以傳遞變量的位址進行處理

package main

import "fmt"

func Test(i *int) { //函數定義,當傳回是為一個時,其不用寫括号
    *i += 1
}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    i := 10
    fmt.Println(i)
    Test(&i)       // 函數調用
    fmt.Println(i) //列印原值。因為函數調用會形成新的空間,其會進行值拷貝,是以其不會影響原來main函數中對應的值
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

7 函數不支援重載

8 在Go中,函數也是一種資料類型,可以指派給一個變量,該變量就是一個函數類型的變量,通過該變量可以對函數進行調用

9 函數是一種資料類型,自然可以作為形參,進行相關的調用操作

package main

import "fmt"

func getSum(n1, n2 int) int {
    return n1 + n2
}

func MyFun(funvar func(int, int) int, num1 int, num2 int) int { //此處傳入進來一個函數,後面的num1 和 num2 是對應的方法
    return funvar(num1, num2)
}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    a := getSum
    fmt.Printf("a 的類型為%T,getSum 的類型為%T\n", a, getSum)
    res := a(10, 20)
    fmt.Println("RES=", res)
    res1 := MyFun(getSum, 40, 50)
    fmt.Println("RES1=", res1)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

10 為了簡化資料類型定義,go支援自定義資料類型

type 自定義資料類型 資料類型 // 了解: 相當于一個别名

類型别名和類型定義見上一篇

https://blog.51cto.com/11233559/2509084

12 使用_ 辨別符,忽略傳回值

13 go 支援可變參數

package main

import "fmt"

func getSum(args ...int) int {  //使用xxx... 來表示多個參數,及可變參數
    sum := 0
    for _, value := range args {  // 此處用于求和
        sum += value
    }
    return sum

}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    res := getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    fmt.Println(res)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

6 遞歸調用

1 基本介紹

函數----遞歸調用

一個函數在函數體又調用了本身,稱為遞歸調用

package main

import (
    "fmt"
)

func getSum(num int) {
    if num > 2 {  //此處num 大于2時,其會不停調用此函數,直至其等于2
        num--
        getSum(num)
    }
    fmt.Println("num=", num)
}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    getSum(10)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

此處存在壓棧問題

當i=2将不成立,此處輸出i=2

當i=3時,由于執行了i--,則此處将輸出i的值為2

一個是直接不成立輸出的結果,另一個是相減後輸出的結果

2 基本執行個體

package main

import (
    "fmt"
)

var sum int = 1 //此處定義一個全局變量

func getSum(num int) int {
    if num == 1 { //此處進行一個退棧操作,及彈出最上面的
        return sum //
    } else {
        sum = getSum(num-1) * num //此處進行遞歸調用
    }
    return sum
}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    res := getSum(10)
    fmt.Println(res)
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

3 遞歸調用遵守的重要原則

1 執行一個函數時,就建立一個新的受保護的獨立空間(新函數棧)

2 函數的局部變量是獨立的,不會影響

3 遞歸必須向退出遞歸的條件逼近,否則就無限遞歸了

4 當一個函數執行完畢後,或者遇到return,就會傳回,遵守誰調用,就将結果傳回給誰

7 init 函數

通常在初始化時被調用
package main

import "fmt"

func getSum() int {
    fmt.Println("變量初始化")
    return 100
}

var Test = getSum() //此處是首先執行的

func init() { //此處是在變量初始化後執行的
    fmt.Println("init 函數初始化")
}
func main() { //定義main函數,一般的檔案執行的入口函數都是main
    fmt.Println("main 函數處理過程") //此處是最後執行的

}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器
如果一個Go檔案同時包含全局變量定義,init 函數和main函數,則執行流程為全局變量定義-----> init 函數------> main 函數

8 匿名函數

Go 支援匿名函數,如果我們某個函數隻希望使用一次,則可考慮匿名函數,匿名函數可實作多次調用
package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    func() int {
        fmt.Println("變量初始化")
        return 100
    }() //定義并進行調用

}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 帶參數的調用

package main

import "fmt"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    a := func(n1, n2 int) int {
        return n1 + n2
    }(10, 20)
    fmt.Println(a)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

3 全局匿名函數

如果将一個匿名函數指派給一個全局便來給你,那麼就會稱為一個全局匿名函數,其可在程式中有效
package main

import "fmt"

var A = func(n1, n2 int) int {
    return n1 + n2
}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    a := A(10, 20)
    fmt.Println(a)

}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

9 閉包

閉包就是一個函數和其他相關的引用環境組合的一個整體,其是引用了自由變量的函數,被引用的自由變量和函數一同存在,及時已經離開了自由變量也不會被釋放或者删除,在閉包中仍可繼續使用這個自由變量及 函數+引用環境=閉包
package main

import "fmt"

func AddUpper() func(int) int { //此處定義傳回一個函數
    var n int = 10
    var str = "hello"
    return func(x int) int {  //此處傳回一個函數,但該函數需要使用外部資源,此時該函數就和該外部資源形成一個整體,被稱為閉包
        n = n + x             //此函數調用外部使用的n和str,并有自己傳遞的x
        str = str + string(n) // 此處進行拼接
        fmt.Println(str)
        return n // 内層函數傳回值
    }
}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    add := AddUpper() // 此處調用傳回内層函數
    num := add(2)     //此處調用内層函數,并傳回和列印對應值
    fmt.Println(num)
    fmt.Println("第二種調用--------------")
    num1 := AddUpper()(2)
    fmt.Println(num1)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

10 defer 函數

1 應用場景

在函數中,程式員需要經常建立資源,為了在函數執行完畢後,及時釋放資源,go的設計者提供defer(延時機制),用于當函數執行完畢時,可以及時釋放資源

業務用途:通常用于關閉檔案和資料庫連結

2 基本使用

defer 會被壓到獨立的棧defer棧中,當函數執行完畢後,會從defer中從先進後出的方式進行出棧執行操作
package main

import "fmt"

func Close() {
    fmt.Println("執行出棧操作")
}
func AddUpper() func(int) int { //此處定義傳回一個函數\
    var n int = 10
    var str = "hello"
    return func(x int) int {
        n = n + x             //此函數調用外部使用的n和str,并有自己傳遞的x
        str = str + string(n) // 此處進行拼接
        fmt.Println(str)
        return n // 内層函數傳回值
    }
}

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    defer Close()
    add := AddUpper() // 此處調用傳回内層函數
    num := add(2)     //此處調用内層函數,并傳回和列印對應值
    fmt.Println(num)
    fmt.Println("第二種調用--------------")
    num1 := AddUpper()(2)
    fmt.Println(num1)
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

11 函數參數的傳遞方式

值類型參數預設就是值傳遞,引用類型參數預設傳遞方式就是引用傳遞

其實,不管是值傳遞函數引用傳遞,傳遞給函數的都是變量的副本,不同的是,值傳遞傳遞的是拷貝,引用傳遞傳遞的是引用位址拷貝,一般來說,位址拷貝效率更高,因為資料量小,而值拷貝的資料大,資料越大,其拷貝效率越低

值類型預設是值傳遞:變量直接存儲值,記憶體通常在棧中配置設定

引用類型預設是引用傳遞:變量存儲的是一個位址,這個位址對應的空間才是真正存儲資料的值,記憶體通常在堆上配置設定,當沒有任何變量引用這個位址時,該位址對應的資料空間就會成為一個垃圾,由GC來進行垃圾回收。

如果希望函數内的變量能夠修改函數外的變量,可以傳入變量的位址,函數内以指針的方式操作,從效果上類似于引用

值類型: 基本資料類型,如int ,float,bool,string,數組和結構體

引用類型: 指針,slice切片,map,管道,interface等都是引用資料類型

12 變量作用域

1 函數内部聲明/定義的變量叫做局部變量,作用僅限于函數内部

2 函數外部聲明的變量叫全局變量,作用域在整個包都有效,如果首字母大寫,則作用域在整個程式中都有效

3 如果變量是在一個代碼塊,則該變量的作用域就是該代碼塊

2 時間和日期相關函數

package main

import (
    "fmt"
    "time"
)

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    now := time.Now() // 擷取目前時間
    fmt.Printf("年 %v\n", now.Year())
    fmt.Printf("月 %v\n", now.Month())
    fmt.Printf("日 %v\n", now.Day())
    fmt.Printf("時 %v\n", now.Hour())
    fmt.Printf("分 %v\n", now.Minute())
    fmt.Printf("秒 %v\n", now.Second())
    //格式化時間
    fmt.Printf("%v\n", now.Format("2006/01/02"))
    fmt.Printf("%v\n", now.Format("2006/01/02  15:04:06")) //此處格式化的時間是不能修改的,必須是2006/01/02  15:04:05 ,否則結果是錯誤的
    fmt.Printf("%v\n", now.Format("2016/01/02  00:00:00")) //此處格式化的時間是不能修改的,必須是2006/01/02  15:04:05 ,否則結果是錯誤的

}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

3 時間常量

Nanosecond Duration=1 //納秒

Microscond =1000 *Nanosecond //微秒

Millisecond = 1000 * Microscond // 毫秒

Second 秒

Minute 分鐘

Hour 小時

package main

import "time"

func main() { //定義main函數,一般的檔案執行的入口函數都是main
    time.Sleep(time.Nanosecond)  //納秒
    time.Sleep(time.Microsecond) // 微秒
    time.Sleep(time.Second)      // 秒
    time.Sleep(time.Minute)      // 分鐘
    time.Sleep(time.Hour)        //小時
}           

2 包

Go語言的源碼複用建立在包(package)基礎之上,Go語言的入口main() 函數所在的包(package)叫main,main包想要引用别的代碼,必須同樣以包的方式進行引用
Go 語言的包與檔案夾一一對應,所有和包相關的操作,都必須依賴于工作目錄(GOPATH)

2 工作目錄 GOPATH

GOPATH 是Go語言中使用的一個環境變量,它使用絕對路徑提供項目的工作目錄,工作目錄是一個工程開發的相對參考目錄。

檢視windows使用 echo %GOPATH%

linux 檢視 echo $GOPATH

在GOPATH指定的工作目錄下,代碼總會儲存在GOPATH/src目錄下。在工程經過go build ,go install 或 go get 等指令後,會将産生的二進制可執行檔案放置在$GOPATH/bin目錄下,生成的中間緩存檔案被儲存在$GOPATH/pkg下

3 多項目工程中使用GOPATH

GOPATH 一般分為全局GOPATH 和項目GOPATH
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器
Global GOPATH 代表全局GOPATH,一般來源于系統環境變量中,Project GOPATH 代表項目所使用的GOPATH,該設定會被儲存在工作目錄.idea目錄下,建議開發時隻寫項目GOPATH,每個項目盡量隻設定一個GOPATH

4 建立包 package

包 package是多個Go源碼的集合,是有種進階的代碼複用方案,Go預設為我們提供了很多包,如fmt,os,io包等,開發者可以根據自己的需求建立自己的包

包的三大作用

1 區分相同名字的函數、變量等辨別符

2 當程式檔案很多時,可以很好的管理項目

3 控制函數,變量等通路範圍,及作用域

包的特性

1 一個目錄下同級檔案歸屬于一個包

2 包可以與其目錄不同名

3 包名為main的包圍應用程式的入口包,編譯代碼沒有main包時,将無法編譯出可執行檔案

5 導出辨別符

在Go語言中,若想在一個包裡引用另一個包裡的辨別符時,必須首先将被引用的辨別符導出,将要導出的辨別符的首字母大寫就可以讓引用者通路這些辨別符
package main

import "fmt"

func Test() {  //首字母大寫可被引用 
    fmt.Println("Test 函數")
}

type Struct struct {
}           

6 導入包(import)

要引用其他包的辨別符,可使用import關鍵字,導入的包使用雙引号包圍,包名從GOPATH開始計算路徑。使用"/"進行分割
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

7 注意事項

1 在給一個檔案打包時,該包下對應一個檔案夾,檔案的包名通常要和檔案所在的檔案夾保持一緻,一般為小寫字母,因為後面的import和調用對應的函數看起來比較友善和協調

2 當一個檔案要使用其他包或者函數或變量時,需要先引入對應的包

8 細節說明

1 引入方式 import "包名"

2 引入方式

import (

"包名1"

"包名2"

"包名3"

)

3 package 指令在檔案第一行,然後是import指令

4 在import包時,路徑從$GOPATH的src開始,不需要帶src

9 導入包後自定義

package main

import (
    myTest "gocode/project01/test"
)

//"D:\go\project\src\gocode\project01\main"
func main() {
    myTest.Test()
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

10 匿名導包,不使用

package main

import (
    "fmt"
    _ "gocode/project01/test"
)

//"D:\go\project\src\gocode\project01\main"
func main() {
    fmt.Println("匿名導包測試")
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

四 錯誤處理

1 Go 語言追求簡單優雅,是以不支援傳統的try...expoty..finally 處理方式

2 Go 中引入的處理方式:defer ,panic ,recover

3 這幾個異常的使用場景可簡單描述為:

panic 抛出異常,然後defer中通過recover捕獲異常進行處理

錯誤處理的好處

進行錯誤處理後,程式不會輕易挂掉,如果加入預警代碼,可以讓程式更加健壯

2 panic 當機

當機可能造成體驗停止、服務中斷

手動觸發panic

package main

import "fmt"

func main() {
    fmt.Println("程式執行中")
    panic("crash")  // 其參數可以是任何類型的
    fmt.Println("程式執行完成")

}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

自動panic

在程式運作過程中,可能由于某些錯誤導緻自動panic

3 recover

無論是代碼運作錯誤由Runtime層抛出的panic崩潰,還是主動觸發的panic崩潰,都可以配合defer和recover 實作錯誤捕捉和恢複,讓代碼在發生崩潰後允許繼續運作
Go 沒有異常系統,其使用panic觸發當機類似于其他語言的抛出異常,那麼recover的當機恢複機制就對應try/catch機制
package main

import (
    "fmt"
)

func Test() {   
    defer func() {  
        err := recover() // 捕獲異常
        if err != nil {
            fmt.Println("error=", err)
        }
    }()
    num1 := 10
    num2 := 0
    res := num1 / num2  // 此處是分母為0,是以會産生異常
    fmt.Printf("%d", res)
}
func main() {
    fmt.Println("函數執行前操作")
    Test()
    fmt.Println("函數執行後的操作")
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

4 error.New

Go 語言支援自定義錯誤,使用error.New 和 panic内置函數完成

1 error.New("錯誤說明"),會傳回一個error類型的值,表示一個錯誤

2 panic内置函數,接受一個interface{} 類型的值作為參數,可以接受error類型的變量,輸出錯誤資訊,并退出程式

package main

import (
    "errors"
    "fmt"
)

func ReadConf(name string) error {
    if name == "test.txt" {
        fmt.Println("結果正确")
        return nil
    } else {
        return errors.New("讀取檔案錯誤,檔案不存在.......")
    }
}

func main() {
    err := ReadConf("golang.txt")
    if err != nil {
        fmt.Println("執行結果為:", err)
    }
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

5 panic 和defer 及recover

package main

import "fmt"

func main() {
    defer func() {
        err := recover() // 捕捉異常
        if err != nil {
            fmt.Println("其crash 結果為:", err)
            fmt.Println("繼續執行下面的語句")
        }
    }()

    panic("程式發生crash")

}           
執行結果為
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

五 容器

變量在一定程度上能滿足函數及代碼的要求,但若要編寫一些複雜的算法,結構和邏輯,就需要更複雜的類型來實作,這些複雜類型一般情況下具有各種形式的存儲和處理資料的功能,将它們稱為"容器"。

2 數組

1 簡介

用于存儲多個相同類型的空間不可變的資料,其是一段固定長度的連續記憶體區域

數組可以存放多個同一類型的資料,數組也是一種資料類型,在Go中,數組是值類型

2 數組的使用步驟

1 聲明數組

2 初始化數組

3 使用數組

3 數組的定義方式

package main

import "fmt"

func main() {
    //基本方式,其預設會自動進行初始化操作,int值為0,float64 值為浮點數,string為空字元串
    var x [6] int
    var y [6]float64
    var z [6]string
    fmt.Printf("類型為:%T,值為:%d\n", x, x)
    fmt.Printf("類型為:%T,值為:%.2f\n", y, y)
    fmt.Printf("類型為:%T,值為:%s\n", z, z)

    var w [3]int = [3]int{1, 2, 3} // 定義并初始化
    fmt.Printf("類型為:%T,值為:%d\n", w, w)
    var n = [3]int{7, 8, 9} //定義并初始化
    fmt.Printf("類型為:%T,值為:%d\n", n, n)
    var m = [...]int{1, 2, 3, 4, 5, 6, 7} // 未知大小的初始化
    fmt.Printf("類型為:%T,值為:%d\n", m, m)
    var q = [...]string{1: "abcd", 2: "golang"} // 根據下标進行初始化
    fmt.Printf("類型為:%T,值為:%s\n", q, q)
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

4 數組的通路和記憶體

數組名[下标]
package main

import "fmt"

func main() {
    var l1 [10] int //數組定義,  10 為大小,int 為數組存儲類型 ,其預設會進行初始化,其初始化值為該類型對應的初始值
    l1[0] = 1
    l1[1] = 2
    for i := 0; i < len(l1); i++ {
        fmt.Println(l1[i])
    }

    //下面表明,數組的首元素記憶體位址就是數組的位址
    fmt.Printf("數組記憶體位址為%p\n", &l1)
    fmt.Printf("數組首元素記憶體位址為%p\n", &l1[0])
    fmt.Printf("數組第二個元素位址為%p\n", &l1[1])
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

結論

1 數組的位址可通過數組名來擷取

2 數組的第一個元素的位址就是數組的首位址

3 數組各個元素的位址間隔是依據數組的類型決定,如Int64->8位元組,int32->4 位元組

5 數組周遊

1 正常周遊

通過基本for循環進行相關周遊操作

2 通過for-range 進行處理

package main

import "fmt"

func main() {
    var x = [4]string{1: "abcd", 2: "goland", 3: "linux", 0: "docker"} //定義數組,其下标從0開始
    for i := 0; i < len(x); i++ {
        fmt.Println(x[i])
    }
    fmt.Println("---------------------")
    for _, value := range x {
        fmt.Println(value)
    }
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

6 注意事項

1 數組是将多個相同類型的資料進行組合的概念,一旦數組聲明/定義了,其長度是固定的,不可修改的

2 var arr []int 是一個切片

3 數組中的元素可以是任意類型,包括值類型和引用類型,但不能混合

4 數組建立後,若沒有指派,則有預設值

數值類型的數組:預設為0

字元類型數組:預設為空

bool 類型的數組,預設為false

6 數組的下标是從0開始的

7 數組下标必須在指定範圍内,否則會報錯panic,數組越界

8 Go的數組屬于值類型,在預設情況下是值傳遞,是以會被進行值拷貝,數組間不會互相影響

9 如果想在其他函數中修改數組,則可使用值傳遞方式進行

10 長度是數組類型的一部分,在傳遞函數參數時,需要考慮數組的長度

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano()) //用于生成随機數字
    var l1 [5] int
    for i := 0; i < len(l1); i++ {
        num := rand.Intn(100) //生成100以内的數字
        l1[i] = num           //生成數組
    }
    fmt.Println("列印數組的值:", l1)
    var l2 [5]int
    for i := 0; i < len(l2); i++ {
        l2[len(l2)-i-1] = l1[i]
    }
    fmt.Println("翻轉結果為:", l2)
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

7 使用案例

1 建立一個類型為byte的26 個元素的數組,分别放置A-Z,并使用for循環通路所有元素并列印

package main

import "fmt"

func main() {
    var l1 [26]byte
    a := 0
    for i := 'A'; i <= 'Z'; i++ {
        l1[a] = byte(i)
        a += 1
    }
    for _, value := range l1 {
        fmt.Printf("%c", value)
    }
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 求一個數組的最大值,并得到對應的下标

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    var l1 [10]int
    // 擷取随機數種子
    rand.Seed(time.Now().UnixNano())
    for i := 0; i < len(l1); i++ {
        num := rand.Intn(100)
        l1[i] = num
    }
    fmt.Println("擷取到數組為:", l1)
    maxindex := 0
    max := l1[0]
    for index, value := range l1 {
        if max < value {
            max = value
            maxindex = index
        }
    }
    fmt.Printf("索引為:%d,對應值為%d", maxindex, max)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

3 求一個數組的平均值

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    var l1 [10]int
    // 擷取随機數種子
    rand.Seed(time.Now().UnixNano())
    for i := 0; i < len(l1); i++ {
        num := rand.Intn(100)
        l1[i] = num
    }
    fmt.Println("擷取到數組為:", l1)
    sum := 0
    for _, value := range l1 {
        sum += value
    }
    fmt.Printf("平均值為:%d", sum/len(l1))
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 數組切片

Go語言切片的内部結構包括位址、大小和容量,切片一般用于快速操作一塊數集合。

基本介紹

1 切片的英文是slice

2 切片是數組的一個引用,是以切片是引用類型,在進行值傳遞時,遵循引用傳遞的規則機制

3 切片的使用和數組相似,周遊切片,通路切片元素和求切片長度都是一樣的

4 切片的長度可以變化的,是以切片是一個動态變化的數組

5 切片的定義基本文法

var 變量名 [] 變量類型

2 切片聲明

1 定義一個切片,然後讓切片引用一個已經建立好的數組

2 通過make來建立切片

var xxx []type = make([],len,[cap])

type 表示類型

len 表示長度大小

cap 表示指定切片的容量

package main

import "fmt"

func main() {
    // 定義方式一,直接使用截取的數組進行處理
    var l1 = [5]int{1, 2, 3, 4, 5}
    l2 := l1[1:4] // 擷取切片并指派
    fmt.Printf("類型為:%T,值為:%d\n", l2, l2)

    //定義方式二,通過标準方式進行定義和執行
    var l3 []int
    l3 = make([]int, 4, 10)  //其容量可省略
    l3[0] = 10
    l3[1] = 20
    fmt.Printf("類型為:%T,值為%d\n", l3, l3)

    //定義方式三,直接定義,并指定其值
    var l4 []int = []int{1, 2, 3, 4, 5}
    fmt.Printf("類型為:%T,值為%d\n", l4, l4)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

3 切片在記憶體中的形式

1 數組在記憶體中的形式是連續的,切片的底層資料結構是一個結構體,slice是一個引用類型,其是通過指針,相關長度和容量組成的

通過make 方式建立切片可以指定切片的大小和容量,如果沒有給切片的各個元素指派,則其會處理成對應的預設值

通過make建立的切片對應的數組是由make底層維護的,對外不可見,隻能通過通路各個元素擷取

4 切片的注意事項

1 切片初始化時 var slice=arr[startindex:endindex],左閉右開

2 切片初始化時,仍然不能越界,範圍在[0-len(arr)]之間,但是可以動态增長

3 cap 是一個内置函數,用于統計切片的容量,及最大可以存放多少個元素

4 切片定義完成後,還不能使用,需要引用一個數組或make一個空間後才能進行相關的使用

5 切片可繼續切片

6 切片的周遊和數組相同,也是通過for循環及for range 進行處理的

5 append

使用append内置函數,可對切片進行動态增加

1 切片append操作的底層原理分析

1 切片的append操作的本質是對數組的擴容

2 Go 底層會建立一個新的數組

3 将原來的資料包含的元素全部拷貝到新的數組上

4 将原來數組的引用拷貝到新的數組上即可,新數組是底層維護,不可見

6 切片的copy

package main

import "fmt"

func main() {
    var l1 = []int{1, 2, 3, 4, 5, 6}
    l1 = append(l1, 7)
    l1 = append(l1, 8)
    fmt.Println(l1)
    var l2 [] int
    l2 = make([]int, 10, 20)
    l2[0] = 1
    l2[1] = 2
    fmt.Printf("長度為:%d,容量為:%d,實際值為:%d\n", len(l2), cap(l2), l2)
    copy(l2, l1) //後面會覆寫前面的
    fmt.Println(l2)
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

7 切片删除元素

切片未提供專門的文法或接口,需要使用切片本身的特性來删除元素
package main

import "fmt"

func main() {
    var x = []int{1, 2, 3, 4, 5, 6, 7}  
    x = append(x[:2], x[3:]...) //切片删除,删除标号為2 的數字 ,此處的append可使用多參數傳值進行處理
    fmt.Println(x)
}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

8 切片練習

1 生成斐波那契數列,并放置到切片中

package main

import "fmt"

func Test(n int) []int {
    l1 := make([]int, n)
    a, b := 0, 1
    for i := 0; i < n; i++ {
        l1[i] = b
        a, b = b, a+b

    }
    return l1
}

func main() {
    l2 := Test(10)
    fmt.Println(l2)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

3 map

Go語言提供的映射關系容器稱為map,map使用散清單(hash)實作

大多數語言中映射關系容器使用兩種算法:散清單和平衡樹

散清單:可簡單描述為一個數組,數組中的每一個元素都是一個清單,根據散列函數獲得每一個元素的特征值,将這些特征值作為映射的鍵,如果特征值重複,就表示元素發生碰撞,碰撞的元素将被放在同一個特征值的清單中進行儲存,散清單查找複雜度O(1),和數組一緻,最壞的情況是O(n),n為元素總數,散列需要盡量避免元素碰撞以提高查找效率,這樣就需要對桶進行擴容,每次擴容,元素都需要被重新放入桶中,較為耗時。
平衡樹:類似于有父子關系的一顆資料樹,每個元素在放入樹時,都需要與一些節點進行比較,平衡樹的查找複雜度始終為O(log n)

2 map 使用的三種方式

package main

import "fmt"

func main() {
    //定義方式一
    var d map[string]string
    d = make(map[string]string)
    d["a"] = "1234"
    d["b"] = "5678"
    fmt.Println(d)
    //定義方式二
    d1 := make(map[string]string)
    d1["a"] = "12345678"
    fmt.Println(d1)
    //定義方式三
    var d2 map[string]string = map[string]string{
        "a": "golang",
        "b": "mysql",
    }
    fmt.Println(d2)
}           
結果為
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

2 多級map

package main

import "fmt"

func main() {
    var d map[string]map[string]string
    d = make(map[string]map[string]string) //初始化外部map
    d["a"] = make(map[string]string)       //初始化内部map
    d["a"]["a"] = "mysql"
    d["a"]["b"] = "linux"
    d["b"] = make(map[string]string) //初始化内部map
    d["b"]["a"] = "mysql"
    d["b"]["b"] = "linux"
    fmt.Println(d)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

3 map 操作

增删改查
清空map:Go 語言中并沒有為map提供任何清空所有元素的函數或方法,清空map唯一的辦法就是重新make一個新的map。
package main

import "fmt"

func main() {
    var d map[string]map[string]string
    d = make(map[string]map[string]string) //初始化外部map
    d["a"] = make(map[string]string)       //初始化内部map
    //增加
    d["a"]["a"] = "mysql"
    d["a"]["b"] = "linux"
    d["a"]["c"] = "golang"
    fmt.Println(d["a"]["c"]) //查詢
    delete(d, "a")           //删除,并删除其類型

    d["b"] = make(map[string]string) //初始化内部map
    //增加
    d["b"]["a"] = "mysql"
    d["b"]["b"] = "linux"
    d["b"]["c"] = "golang"
    for _, value := range d {
        fmt.Println(value)
    }
    d["b"]["a"] = "111"
    d["b"]["b"] = "222"
    d["b"]["c"] = "333"
    for _, value := range d {
        fmt.Println(value)
    }

}           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

4 map 使用細節

1 map是引用類型,遵循值傳遞過程,在一個函數接受一個map,修改後,直接修改原來的map

2 map 的容量到達後,想在增加元素,會自動擴容,并不會發生panic,也就是說map能動态的增證鍵值對

3 map 的value經常使struct 類型,更加适合管理複雜資料

5 map 排序

1 golang 沒有專門針對map的key 進行排序的方式

2 golang中的map預設是無序的,其也不是按順序添加的,每次周遊,其結果都可能不同

3 golang中的map排序,是先将key進行排序,然後根據key取出value并進行排序

4 map 切片

切片資料類型若是map。則map可進行動态變化了
package main

import "fmt"

func main() {
    var d []map[string]string //此處相當于切片套map
    d = make([]map[string]string, 2)
    if d[0] == nil {
        d[0] = make(map[string]string)
        d[0]["a"] = "linux"
        d[0]["b"] = "windows"
    }
    fmt.Println(d)
}
           
Go語言基礎(二)一 運算符二 控制語句三 函數和包四 錯誤處理五 容器

5 list

清單是一種非連續存儲的容器,由多個節點組成,節點通過一些變量記錄彼此之間的關系,清單有多種方式實作,如單連結清單,雙連結清單等

2 初始化清單

list 的初始化有兩種方式: New 和聲明,兩種方式的初始化效果都是一緻的。

1 通過container/list 包的New方法初始化list

2 通過聲明初始化

l1 := list.New() // 初始化清單
    var l2 list.List
           
清單與切片和map不同的是,清單并沒有具體元素類型的限制,是以,清單的元素可以是任意類型,這既帶來了便利,也引來了一些問題,如給一個清單中放入了非期望類型的值,在取出值後,将interface{} 轉換為期望類型時将會引發當機

3 在清單中插入元素

雙連結清單支援從隊列前方或後方插入元素,分别對應的方法是PushFront和PushBack
提示: 這兩個方法都會傳回一個*List.Element結構,如果在以後的使用中需要删除插入的元素,則隻能通過*list.Element配合Remove()方法進行删除,這種方式可讓删除更加有效率,也是雙向連結清單特性之一。
方法 功能
InsertAfter(v interface{},mark *Element) *Element 在mark之後插入元素,mark點由其他插入的函數提供
InsertBefore(v interface{},mark *Element) *Element 在mark點之前插入元素,mark點由其他插入函數提供
PushBackList(other *List) 添加other列到元素尾部
PushFrontList(other *List) 添加other列元素到頭部
l1 := list.New() // 初始化清單

    l1.PushBack("fist") //從後面插入
    l1.PushFront(64)    //從前面插入           

4 從清單中删除元素

package main

import (
    "container/list"
    "fmt"
)

func main() {
    l1 := list.New() // 初始化清單

    l1.PushBack("fist") //從後面插入
    l1.PushFront(64)    //從前面插入
    element := l1.PushBack("second")
    l1.InsertBefore(1, element) //在second之前添加1
    l1.InsertAfter(2, element)  //在second之後添加2
    //移除 second
    l1.Remove(element)

    for i := l1.Front(); i != nil; i = i.Next() { //l1.Front() 表示第一個元素,及頭元素,周遊時,值不為空即可,是以其條件是之不為空,每次周遊都需要調用Next
        fmt.Println(i.Value)
    }
}