日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Go 学习笔记(61)— Go 高阶函数、函数作为一等公民(函数作为输入参数、返回值、变量)的写法

發布時間:2023/11/28 生活经验 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go 学习笔记(61)— Go 高阶函数、函数作为一等公民(函数作为输入参数、返回值、变量)的写法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

函數在 Go 語言中屬于“一等公民(First-Class Citizen)”擁有“一等公民”待遇的語法元素可以如下使用

  • 可以存儲在變量中;
  • 可以作為參數傳遞給函數;
  • 可以在函數內部創建并可以作為返回值從函數返回;

1. 函數可以存儲在變量中

var (myFprintf = func(w io.Writer, format string, a ...interface{}) (int, error) {return fmt.Fprintf(w, format, a...)}
)func main() {fmt.Printf("%T\n", myFprintf) // func(io.Writer, string, ...interface {}) (int, error)myFprintf(os.Stdout, "%s\n", "Hello, Go") // 輸出Hello,Go
}

2. 作為參數傳入函數

標準庫 time 包的 AfterFunc 函數,就是一個接受函數類型參數的典型例子。你可以看看下面這行代碼,這里通過 AfterFunc 函數設置了一個 2 秒的定時器,并傳入了時間到了后要執行的函數。這里傳入的就是一個匿名函數:

time.AfterFunc(time.Second*2, func() { println("timer fired") })
package mainimport "fmt"type CalculateType func(int, int) intfunc add(a, b int) int {return (a + b)
}func mul(a, b int) int {return (a * b)
}func Calculate(a, b int, f CalculateType) int {return f(a, b)
}
func main() {a, b := 2, 4fmt.Println(Calculate(a, b, add))	// 6fmt.Println(Calculate(a, b, mul))	// 8
}

以上例子,Calculatef 參數類型為 CalculateTypeaddmul 函數具有和 CalculateType 函數類型相同的參數和返回值,因此可以將 addmul 函數作為參數傳入 Calculate 函數中。

3. 支持在函數內創建并通過返回值返回

func setup(task string) func() {println("do some setup stuff for", task)return func() {println("do some teardown stuff for", task)}
}func main() {teardown := setup("demo")defer teardown()println("do some bussiness stuff")
}

4. 擁有自己的類型

在前面講解函數聲明時,我們曾得到過這樣一個結論:每個函數聲明定義的函數僅僅是對應的函數類型的一個實例,就像

var a int = 13

這個變量聲明語句中的 a,只是 int 類型的一個實例一樣。換句話說,每個函數都和整型值、字符串值等一等公民一樣,擁有自己的類型,也就是我們講過的函數類型。

下面代碼中的 HandlerFuncvisitFunc 就是 Go 標準庫中,基于函數類型進行自定義的類型:

// $GOROOT/src/net/http/server.go
type HandlerFunc func(ResponseWriter, *Request)// $GOROOT/src/sort/genzfunc.go
type visitFunc func(ast.Node) ast.Visitor

Go 語言中,可以把函數作為一種變量,用 type 去定義它,那么這個函數類型就可以作為值傳遞,甚至可以實現方法,我們可以利用這一特性進行類型轉換。

作為值傳遞的條件是類型具有相同的參數以及相同的返回值。Go 語言的類型轉換基本格式如下:

type_name(expression)

代碼實現

package mainimport "fmt"type CalculateType func(int, int)func (c *CalculateType) Server() {fmt.Println("這是函數類型")
}func add(a, b int) {fmt.Println(a + b)
}func mul(a, b int) {fmt.Println(a * b)
}func main() {a := CalculateType(add)b := CalculateType(mul)a(2, 4)b(2, 4)a.Server()b.Server()
}

輸出結果:

6
8
這是函數類型
這是函數類型

5. 高階函數

先來說說什么是高階函數?簡單地說,高階函數可以滿足下面的兩個條件之一:

  • 接受其他的函數作為參數
  • 把其他的函數作為結果返回

只要滿足了其中任意一個特點,我們就可以說這個函數是一個高階函數。高階函數也是函數式編程中的重要概念和特征。

高階函數和閉包

  • 所謂閉包就是一個函數體內部引用了一個外部的變量
  • 高階函數和函數式編程的特點就是 支持函數作為參數或者返回值

示例:

package mainimport ("errors""fmt"
)type operate func(x, y int) int// 方案1。
func calculate(x int, y int, op operate) (int, error) {// 函數類型屬于引用類型,它的值可以為nil,而這種類型的零值恰恰就是nil。if op == nil {return 0, errors.New("invalid operation")}return op(x, y), nil
}// 方案2。
type calculateFunc func(x int, y int) (int, error)func genCalculator(op operate) calculateFunc {/*閉包定義:在一個函數中存在對外來標識符的引用。所謂的外來標識符,既不代表當前函數的任何參數或結果,也不是函數內部聲明的,它是直接從外邊拿過來的。*//*此處 return 函數是個閉包,它里面使用的變量op既不代表它的任何參數或結果也不是它自己聲明的,而是定義它的genCalculator函數的參數,所以是一個自由變量。*/return func(x int, y int) (int, error) {if op == nil {return 0, errors.New("invalid operation")}return op(x, y), nil}
}func main() {// 方案1。x, y := 12, 23op := func(x, y int) int {return x + y}result, err := calculate(x, y, op)fmt.Printf("The result: %d (error: %v)\n",result, err)result, err = calculate(x, y, nil)fmt.Printf("The result: %d (error: %v)\n",result, err)// 方案2。x, y = 56, 78add := genCalculator(op)result, err = add(x, y)fmt.Printf("The result: %d (error: %v)\n",result, err)
}

Go 可以定義函數類型的變量,函數類型的變量可以被調用。定義函數類型變量示例代碼:

package mainimport ("fmt""reflect"
)func main() {var f = func(str string) {fmt.Println("hello", str)}fmt.Println("類型是:", reflect.ValueOf(f).Kind())f("func type")
}

輸出結果:

類型是: func
hello func type

等價于

package mainimport ("fmt""reflect"
)func demo(str string) {fmt.Println("hello", str)
}
func main() {var f func(str string)f = demofmt.Println("類型是:", reflect.ValueOf(f).Kind())f("func type")
}

函數類型變量也可以當做參數傳遞給另一個函數,然后在另一個函數中執行,示例代碼如下:

package mainimport ("fmt""reflect"
)func exec(f func(str string)) {f("func type")
}func main() {var f = func(str string) {fmt.Println("hello", str)}fmt.Println("類型是:", reflect.ValueOf(f).Kind())exec(f)
}

輸出結果:

類型是: func
hello func type

exec 函數接收一個函數類型的參數,那么是不是只要是函數,就可以被當做參數傳入到 exec 中嗎?

答案是:不行。從 exec 函數的參數列表可知,exec 接收一個函數類型參數,這個函數類型參數接收一個字符串類型的參數。

總結

以上是生活随笔為你收集整理的Go 学习笔记(61)— Go 高阶函数、函数作为一等公民(函数作为输入参数、返回值、变量)的写法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。