函式
介紹
有其他語言基礎的話就沒啥介紹的,基本語法:
func functionName(形參列別) (返回值列表){
執行語句
return 返回值列表
}
例子:
func getSum(n1 int, n2 int) int {
var res int = n1 +n2
return res
}
包
實際就建立不同的資料夾,存放程式檔案。Go的每一個檔案都輸屬於一個包的,go以包的形式來管理檔案和目錄結構。
作用:
- 區分相同名字的函式與變數符
- 當專案比較複雜時,便於管理專案
- 控制函式、變數的訪問範圍,也就是作用域
打包指令:package 包名
引入指令:import "包路徑"
注意事項:
- 包中的函式與變數如果首字母大寫則是對外公開的,稱為該函式可以匯出,如果小寫則是非公開的
- 包名通常應該與所處資料夾保持一致,不一致也可以
- 同一個包下面,不能有相同的函式名
- main包只能有一個
init函式
每一個原始檔都可以包含一個init函式,該函式在main函式執行之前被Go執行框架呼叫。一般完成初始化工作 。
在一個檔案中如果同時含有全域性變了定義、init函式與main函式,那麼執行流程是:
全域性變數定義->init函式->main函式
當一個檔案引用的包裡面有全域性變數定義與init函式時,先執行包的全域性變數定義與init函式,再執行該檔案的全域性變數定義與init函式,最後執行main函式
匿名函式
如果希望某一個函式只使用一次,可以考慮匿名函式。當然匿名函式也可以實現多次使用。什麼場景下會使用匿名函式呢?也就是閉包。一個最常用的場景就是程式執行出現錯誤的時候,我們要去恢復,一般是透過關鍵字defer,我們要有一個程式邏輯,在某些特定場景下執行的,但是我沒有必要為整個邏輯定義函式,通常這樣就可以使用閉包來做。
- 使用方式1:在定義時就使用
res1 := func (n1 int, n2 int) int {
return n1+n2
}(5, 6)//傳入引數
fmt.Printf("res1的值是%d\n", res1) //結果為11
- 使用方式2:將匿名函式賦給一個變數,透過變數呼叫函式,可以反覆呼叫(用處感覺不是很大,用處是可以在main()中定義函式)
func main(){
a = func (n1 int, n2 int) int { //此時a的資料型別為函式型別
return n1+n2
}
res2 := a(5, 6) //a是變數,不是函式名
fmt.Printf("res2的值是%d\n", res2) //結果為11
res3 := a(10, 6) //a是變數,不是函式名
fmt.Printf("res3的值是%d\n", res3) //結果為16
}
- 使用方式3:全域性匿名函式:將匿名函式賦給一個全域性變數,透過變數呼叫函式
閉包
閉包是一個函式和與其相關的引用環境組成的整體。例子:
package main
import (
"fmt"
)
func AddUpper() func (int) int {
var n int = 10
return func(x int) int {
n = n + x;
return n
}
}
func main(){
f := AddUpper()
fmt.Println(f(1)) //結果為11
fmt.Println(f(2)) //結果為13
fmt.Println(f(3)) //結果為16
g := AddUpper()
fmt.Println(g(2)) //結果為12
}
// AddUpper是一個函式,其返回的資料型別是函式型別。
// 返回的匿名函式就是閉包的函式。
// n是引用的環境,構成了一個整體
// 可以理解為閉包是一個類,環境就是成員變數,匿名函式是唯一的一個成員函式。
defer
在函式中需要建立資源(redis,MySQL連結或者檔案、鎖資源),為了在函式執行完畢後及時的釋放資源,Go的設計者提供了defer機制。
package main
import "fmt"
func sum(n1 int, n2 int) int {
// 當執行到defer時,會將defer後面的語句壓入到一個獨立的棧中,暫時不執行
// 當函式執行完畢後,再從棧中按照先入後出的方式出棧,並一一執行
// 在defer將語句入棧時,也會將語句相關的資源與變數複製至棧中,在以下兩個語句中分別就是n1, n2
defer fmt.Println("ok1 n1=", n1)
defer fmt.Println("ok2 n1=", n2)
res := n1 + n2
fmt.Println("ok3 res=", res)
return res
}
func main(){
res := sum(10, 20)、
fmt.Println("res的值是", res)
}
以上程式碼的輸出結果為:
ok3 res= 30
ok2 n1= 20
ok1 n1= 10
res的值是 30
函式引數的傳遞方式
-
值傳遞:基本的資料型別,int,float,string,陣列,結構體,記憶體一般在棧上分配
-
引用傳遞:指標,切片,map,管道chan,interface,本質上是地址複製,地址複製效率更高。記憶體通常在堆上分配
func test(n1 *int, n2 *int) int{
*n1 = *n2 + 5
return *n1
}
變數作用域
- 函式內部宣告或者定義的變數叫區域性變數,作用域僅限於函式內部
- 函式外部宣告或者定義的變數為全域性變數,作用域在整個包都有效,如果首字母大寫,這在整個程式都有效
- 在if/for中宣告的變數只在這個if/for程式碼塊中有效
- 就近原則:if/for > 函式內部 > 全域性變數
常用的字串函式
// 統計字串的長度,不用調包,是內建函式
// 返回v中位元組的數量,不是元素的數量,是位元組的數量,一共漢字是三個位元組
func len(v type) int
// 字串遍歷,同時處理有中文的問題
r := []rune(str) //將字串轉為了切片
// 字串轉整數,需要用包strconv, 如果失敗會產生erorr
str1 :="12345"
n err := strconv.Atoi(str1)// n==13245
if err != nil{
fmt.Println("轉換錯誤:", err)
}else{
fmt.Println("n的值是:", n)
}
// 整數轉為字串
str2 = strconv.Itoa(123)
fmt.Println("str的值是:", str2)
// 查詢一個字串是否含有某一個子串
strings.Contains(str1, str2)//返回值是bool型別