函數 (1)函數的定義 函數使用func進行定義 函數是基本的代碼塊,用於執行一個任務 Go語言至少有一個main函數 函數聲明告訴了編譯器函數的名稱,返回類型和參數 //1.無參數無返回值函數的定義 func test1(){ fmt.Println("無參數無返回值函數的定義\n") } //2 ...
函數
(1)函數的定義
- 函數使用func進行定義
- 函數是基本的代碼塊,用於執行一個任務
- Go語言至少有一個main函數
- 函數聲明告訴了編譯器函數的名稱,返回類型和參數
//1.無參數無返回值函數的定義
func test1(){
fmt.Println("無參數無返回值函數的定義\n")
}
//2.有參數無返回值函數的定義
func test2(str string){
fmt.Println("有參無返回值:%s\n",str)
}
//3.多參無返回值
func test3(a,b int) int{
return a+b
}
//4.多個返回值
func test4(a,b int) (int,int){
return a,b
}
//函數的調用
fun main()
{
test1()
test2("WY")
test3(1,2)
test4(5,6)
}
(2)函數的可變參數
- 一個函數的參數類型確定,但個數不確定時可以使用可變參數
- 定義可變參數時,使用**... **符號
- 可變參數本質上是保存到了一個數組當中
- 註意:可變參數要放在參數定義的最後;一個函數的參數列表最多只有一個可變參數
//可變參數函數定義
func add_sum(num...int){
sum := 0
for _,i := range num{
sum += num[i-1]
}
}
(3)參數傳遞的類型
-
參數分為兩種類型:
- 值類型-操作的是數據本身,例如我們常用的:int,string,bool,float64,array
- 引用類型的數據-操作的是數據的地址:例如slice,map,chan
-
值傳遞在調用完函數後不會修改原本值傳遞參數的值
//定義一個函數,進行值傳遞實驗
func update(arr2 [4]int){
fmt.Println("arr2接收到的數據:"arr2)
arr2[0] = 100
fmt.Println("arr2修改後的數據:"arr2)
}
//定義主函數,調用update進行值傳遞觀察
func main(){
arr := [4]int{1,2,3,4} //定義一個數組
fmt.Println(arr)
//傳遞值
update(arr)
fmt.Println("調用後的數據") //這裡的結果為[1,2,3,4]
}
- 引用類型數據的參數傳遞在調用完函數後會修改原本值傳遞參數的值
package main
import "fmt"
/*
=====引用傳遞=======
1、定義一個切片
2、通過方法修改切片裡面的數據
3、列印輸出修改後值
*/
func main() {
arr := []int{1, 2, 3, 4} //定義一個切片
fmt.Println("調用修改前的數據", arr)
updata2(arr)
fmt.Println("調用修改後的數據", arr)
}
func updata2(arr []int) {
fmt.Println("arr接受的數據:", arr)
arr[0] = 100
fmt.Println("arr修改後的數據:", arr)
}
/*
調用修改前的數據 [1 2 3 4]
arr接受的數據: [1 2 3 4]
arr修改後的數據: [100 2 3 4]
調用修改後的數據 [100 2 3 4]
*/
(4)函數變數的作用域
- 作用域:變數可以使用的範圍
- 局部變數:函數內部定義的變數,叫做局部變數,只能在定義該變數的函數內使用
- 全局變數:函數外部定義的變數,叫做全局變數,可以在任何函數裡面使用
package main
import "fmt"
// 全局變數 所有函數都可以使用
var num int = 30
func main() {
// 局部變數 temp 只能在定義temp的方法中使用
temp := 100
if b := 1; b < 10 {
temp := 30
fmt.Println("局部變數b:", b)
fmt.Println("局部變數temp:", temp)
}
fmt.Println(temp)
// 調用f1,f2方法
f1()
f2()
}
func f1() {
fmt.Println(num)
}
func f2() {
fmt.Println(num)
}
(5)遞歸函數
- 遞歸函數的定義:可以自己調用自己的函數
- 遞歸函數必須要有一個出口,否則會陷入死迴圈
package main
import "fmt"
/*
1、創建一個求和函數 getsum
2、給遞歸函數一個出口
3、創建對象來接收函數
*/
func main() {
//3、創建對象來接收函數
sum := getSum(5)
fmt.Println(sum)
}
//1、創建一個求和函數 getsum
func getSum(n int) int {
//2、給遞歸函數一個出口
if n == 1 {
return 1
}
return getSum(n-1) + n
}
(6) defer延遲函數
- defer函數可以延遲一個函數的執行
在以下代碼塊中,控制台列印的結果是1 2 4 3,因為defer將f(3)延遲到最後執行了
package main
import "fmt"
func main() {
f("1")
f("2")
defer f("3") //將該函數延遲到最後執行
f("4")
}
func f(s string) {
fmt.Println(s)
}
- 如果使用了多個defer語句,則函數執行到最後時,這些defer語句會按照逆序執行
package main
import "fmt"
func main() {
f("1")
f("2")
defer f("3")
f("4")
defer f("5")
f("6")
defer f("7")
f("8")
}
func f(s string) {
fmt.Println(s)
}
/*
輸出結果:
1
2
4
6
8
7
5
3
*/
(7)func數據類型詳解
-
在go語言中,函數是複合類型,本質上可以看作是一個特殊的變數
-
func本身就是一個數據類型,func定義的函數之間可以相互賦值
package main
import "fmt"
/*
fun本身就是一個數據類型
1、創建一個函數
2、在mian方法定義相同類型的函數
3、將定義後的函數賦值個另一個函數
*/
func main() {
//2、在mian方法定義相同類型的函數
var fg func(int, int)
//3、將定義後的函數賦值個另一個函數
fg = ff //將fg函數定義為ff函數相同的功能
fg(1, 2) //最後輸出的結果為 1 2
}
//1、創建一個函數
func ff(a, b int) {
fmt.Println(a, b)
}
(8)匿名函數
- 什麼是匿名函數:匿名函數就是沒有名字的函數
匿名函數的創建
//這就是一個匿名函數,但如果直接放在代碼塊中不調用則編譯器會報錯
func() {
fmt.Println("匿名函數")
}
//帶參匿名函數
func(a,b int){
fmt.Println("帶參匿名函數")
}
//帶參且具有返回值函數
func(a,b int) int{
fmt.Println("帶參和返回值匿名函數")
return a+b
}
匿名函數多種調用方式
func main(){
//方式1:用一個變數調用匿名函數
f3 := func(){
fmt.Println("匿名函數的調用方式1")
}
f3() //調用匿名函數
//方式2:創建匿名函數後直接調用func(),在{}後直接添加()進行調用
func(){
fmt.Println("匿名函數調用方式2")
}()
//帶參匿名函數的調用
func(a,b int){
fmt.Println("帶參匿名函數調用")
}(1,2)
//帶參和返回值匿名函數調用
f5 := func(a,b int) int{
fmt.Println("帶參和返回值匿名函數")
return a+b
}(1,2)
fmt.Println(f5) //列印調用結果
}
(9)高階函數和回調函數
-
回調函數:將一個函數作為另一個函數的參數
-
若將fun1函數作為fun2函數的參數,則fun2叫做高階函數,fun1稱為回調函數
package main
import "fmt"
//1、創建一個高階函數oper,傳如一個函數類型的參數且有返回值
func oper(a, b int, fun func(int, int) int) int {
r2 := fun(a, b)
return r2
}
//2.創建其他方法的函數
func add(a, b int) int {
return a + b
}
//創建其他方法的函數
func sub(a, b int) int {
return a - b
}
func main() {
r1 := add(1, 2)
fmt.Println(r1)
//3、調用高階函數時,傳入其他方法的函數
r3 := oper(3, 4, add)
fmt.Println(r3)
r4 := oper(8, 4, sub)
fmt.Println(r4)
}
(10)閉包
- 一個外層函數中,有內層函數,該內層函數中,會操作外層函數的局部變數並且該外層函數的返回值就是這個內層函數。
- 這個內層函數和外層函數的局部變數,統稱為閉包結構。
- 局部變數的生命周期就會發生改變,正常的局部變數會隨著函數的調用而創建,隨著函數的結束而銷毀但是閉包結構中的外層函數的局部變數並不會隨著外層函數的結束而銷毀,因為內層函數還在繼續使用
package main
import "fmt"
// 定義一個閉包函數。func() int是局部變數和返回值fun的類型
func increment() func() int {
//局部變數
i := 0
//定義一個匿名函數 給變數自增並返回
fun := func() int {
//內層函數,沒有執行
i++
return i
}
return fun
}
func main() {
//定義一個變數 接受返回值(函數)
r1 := increment() //創建函數
fmt.Println(r1) //這裡列印的是地址,因為r1還沒有執行
v1 := r1()
fmt.Println(v1)
//不斷調用自增+1
v2 := r1()
fmt.Println(v2)
//不斷調用自增+1
fmt.Println(r1())
fmt.Println(r1())
fmt.Println(r1())
fmt.Println(r1())
fmt.Println("====================")
// ==========================================================
r2 := increment() //創建函數,i也會清零
v3 := r2()
fmt.Println(v3)
fmt.Println(r2())
fmt.Println(r2())
fmt.Println(r2())
fmt.Println(r1()) //這裡調用的是r1,因此i值還是原來r1的i值,這裡自增後為7
fmt.Println(r2())
}
/*
輸出結果:
0xd5ea00
1
2
3
4
5
6
====================
1
2
3
4
7
5
*/