Go語言中的函數是組織代碼的最小單元,用于封裝一段代碼,完成特定的功能。函數的使用可以減少代碼冗余,提高代碼的可讀性和可維護性。
函數的基本定義和語法
在Go語言中,定義一個函數的基本語法如下:
func functionName(parameter1 type1, parameter2 type2, ...) (returnType, returnType...){
// 函數體
return value1,value2...
// 返回值
}
-
func
:關鍵字用于定義函數。 -
functionName
:函數名,用于唯一標識該函數。 -
parameter1 type1, parameter2 type2, ...
:參數列表,函數可以接收零個或多個參數。每個參數由參數名和參數類型組成,多個參數之間使用逗號分隔。 -
returnType
:返回類型,指定函數的返回值的數據類型。可以返回零個或多個值 -
return value
:返回值,如果有返回值,則需要使用return
語句將結果返回給調用者。
如果沒有返回值,可以不寫 return。如果寫了,代表退出整個函數。
命名返回值
go語言支持為返回值命名。這些命名的返回值可以在函數體內部直接使用,而不需要顯式地返回它們。這種方式特別適用于在函數執行過程中逐步構建或計算返回值,或者在滿足特定條件時提前返回。
func sum(a, b int) (result int) {result = a + b // 直接賦值給命名返回值if result >= 0 {return // 提前返回,此時 result 自動返回}// 可以繼續處理其他邏輯。比方返回值小于0,打印一句提醒,并把結果處理成正數。fmt.Println("您的計算結果小于0,已經為您取絕對值")result = result * -1return // 最終返回 result
}func main() {fmt.Println(sum(5, 7)) // 輸出: 12fmt.Println(sum(-3, -7)) // 輸出: (您的計算結果小于0,已經為您取絕對值 10)
}
函數的調用方式
函數的調用是通過函數名加上括號實現的。如果函數有參數,調用時需要在括號內傳入實際的參數值。例如:
func add(a int, b int) int {
return a + b
}
func main() {result := add(3, 5) // 調用add函數,傳入3和5作為參數fmt.Println("3 + 5 =", result) // 輸出結果
}
定義多返回值函數
你可以在函數定義時,通過在參數列表后直接指定返回值的類型來實現多返回值。每個返回值類型之間用逗號分隔。
使用函數時候,你只對函數的某些返回值感興趣,可以使用下劃線(_)來忽略不需要的返回值。
func divide(a, b int) (int, int) {r := a / breturn r, a % b // 可以使用變量 返回,也可以使用表達式計算結果返回
}func main() {_, remainder := divide(10, 3) // 只關心余數,忽略商數fmt.Println("Remainder:", remainder)
}
//注意,定義無返回值函數,不能使用變量接收 函數的返回。
可變參數
go語言,不支持給參數設置默認值。
但是支持,可變參數。使用...type 定義。把收集到多余的實參,存放到對應類型的切片中。
匿名函數
匿名函數是通過 func
關鍵字直接定義的,沒有指定函數名。你可以將匿名函數賦值給一個變量,或者直接調用它。
myFunction := func(x, y int) int {return x + y}// 調用匿名函數result := myFunction(5, 10)fmt.Println(result) // 輸出: 15// 直接調用匿名函數result := func(x, y int) int {return x + y}(5, 10)fmt.Println(result) // 輸出: 15
go函數作為參數類型
函數作為參數的使用方式非常簡單,我們只需要將需要傳遞的函數作為參數傳遞給另一個接受這個函數作為參數的函數即可。
如果邏輯代碼很復雜,上邊代碼就很 臃腫,不容易讓人理解。我們可以。把函數簽名聲明為一個類型。
函數閉包
閉包通常涉及到定義一個函數,該函數在其內部定義了另一個函數,并將該函數返回出去,返回出去的函數可以訪問外部函數的變量。
package mainimport "fmt"func outerFunction() func() int {var x = 10return func() int {x++return x}
}func main() {myClosure := outerFunction() // 獲取閉包fmt.Println(myClosure()) // 輸出: 11fmt.Println(myClosure()) // 輸出: 12
}
函數總結
函數的特性
Go語言的函數具有以下特性:
-
支持不定參數:可以使用
...
來表示不定參數,允許函數接收任意數量的參數。 -
支持多返回值:可以返回多個結果,例如錯誤信息和結果值。
-
支持命名返回參數:可以在函數內部給返回值命名,方便處理返回值。
-
支持匿名函數和閉包:可以定義匿名函數并在需要時調用。
-
函數也是一種類型:一個函數可以賦值給變量,實現更靈活的調用。
-
不支持嵌套、重載和默認參數:Go語言不支持在一個包中定義兩個名字相同的函數、函數的重載以及默認參數
defer 延遲調用
defer
的核心作用是延遲調用函數或方法,
這意味著defer
語句所指定的函數調用會被推遲到當前函數返回之前執行,無論函數是正常返回還是因發生異常而返回。
當一個函數中有多個defer語句時,它們會按照后進先出的順序執行。也就是說,最后一個defer語句所指定的函數會最先被調用,而第一個defer語句所指定的函數最后被調用。例如:
遞歸是一種常見的編程技術,它允許函數直接或間接地調用自身。
遞歸的核心思想在于將一個大問題分解為若干個小問題,然后逐步解決這些小問題,最終達到解決整個大問題的目的。
特點:
??自身調用:原問題可以分解為子問題,子問題和原問題的求解方法是一致的,即都是調用自身的同一個函數。
??終止條件:遞歸必須有一個終止的條件,即不能無限循環地調用本身。
??簡潔但效率不高:遞歸算法解題通常顯得很簡潔,但遞歸算法解題的運行效率較低,因為數據存入堆棧中,等待函數調用結束后再取出,會增加性能消耗。另外堆棧還存在溢出的風險。
計算階乘的遞歸函數:
func factorial(n uint) uint {
????????if n == 0 {
????????????????return 1 // 遞歸終止條件
?????????? }
????????return n * factorial(n-1) // 調用自身
}
-
. 構建遞歸調用堆棧
每次函數調用自身時,一個新的調用實例被推入調用堆棧。直到達到基本情況,才開始從堆棧中逐個返回結果。這個過程類似于函數調用鏈的構建和解析。
-
避免無限遞歸和棧溢出
正確設置遞歸終止條件,確保每個遞歸函數都有清晰定義的基本情況是非常重要的,否則可能會導致無限遞歸,進而耗盡棧空間,導致程序崩潰(棧溢出)。
-
優化遞歸(尾遞歸優化)
在某些情況下,可以通過尾遞歸優化來提高效率。尾遞歸是指在函數的最后一步調用自身的情況。Go語言在某些情況下可以優化尾遞歸,使其表現得像循環一樣,從而避免棧溢出。
遞歸是實現諸如樹遍歷、排序算法(如快速排序)、 斐波那契數列,圖與樹的遍歷搜索等多種算法的有效方式
快速排序的實現
package mainimport ("fmt"
)// quickSort 對數組 arr 進行快速排序
func quickSort(arr []int) []int {if len(arr) < 2 {// 基本情況:數組只有一個元素或為空,無需排序return arr}left, right := 0, len(arr)-1// 選擇最右邊的元素作為基準值pivot := right// 將數組分為小于基準和大于基準的兩部分newPivot := partition(arr, left, right, pivot)// 遞歸排序左右兩部分quickSort(arr[:newPivot])quickSort(arr[newPivot+1:])return arr
}// partition 對數組進行劃分,返回新的基準位置
func partition(arr []int, left, right, pivot int) int {pivotValue := arr[pivot]// 將基準值交換到最右邊,即最后一個位置arr[pivot], arr[right] = arr[right], arr[pivot]storeIndex := leftfor i := left; i < right; {if arr[i] < pivotValue {arr[i], arr[storeIndex] = arr[storeIndex], arr[i]storeIndex++}i++}// 將基準值放到正確的位置上arr[storeIndex], arr[right] = arr[right], arr[storeIndex]return storeIndex // 返回新的基準位置
}func main() {arr := []int{10, 7, 8, 9, 1, 5}fmt.Println("Original array:", arr)sortedArr := quickSort(arr)fmt.Println("Sorted array: ", sortedArr)
}
go內置排序函數
sort.Slice
進行通用排序
從Go 1.8開始,sort
包引入了Slice
函數,它允許你直接對切片進行排序,而無需實現任何接口。
package mainimport ("fmt""sort"
)func main() {a := []int{3, 1, 4, 1, 5, 9}sort.Slice(a, func(i, j int) bool { return a[i] < a[j] }) // 使用閉包進行排序邏輯定義fmt.Println(a) // 輸出: [1 1 3 4 5 9]
}