golang教程之一類函式
文章目錄
一類函式
什麼是一類函式?
支援一類函式的語言允許將函式分配給變數,作為引數傳遞給其他函式並從其他函式返回。 Go支援一類函式。
在本教程中,我們將討論第一類函式的語法和各種用例。
匿名函式
讓我們從一個簡單的例子開始,它將一個函式賦給變數。
package main
import (
"fmt"
)
func main() {
a := func() {
fmt.Println("hello world first class function")
}
a()
fmt.Printf("%T", a)
}
在上面的程式中,我們為變數a
分配了一個函式。這是將函式賦值給變數的語法。 如果您仔細注意,分配給a
的函式沒有名稱。 這些函式稱為匿名函式,因為它們沒有名稱。
呼叫此函式的唯一方法是使用變數a
。 我們已經在下一行完成了這項工作。 a()
呼叫該函式,這將列印hello world first class function
a
的型別。 這將列印func()
。
執行此程式將輸出
hello world first class function
func()
也可以在不將其分配給變數的情況下呼叫匿名函式。 讓我們看看如何在以下示例中完成此操作。
package main
import (
"fmt"
)
func main() {
func() {
fmt.Println("hello world first class function")
}()
}
在上面的程式中,匿名函式在第8行中定義。在函式定義之後,我們在第10行中呼叫函式using()
hello world first class function
也可以像任何其他函式一樣將引數傳遞給匿名函式。
package main
import (
"fmt"
)
func main() {
func(n string) {
fmt.Println("Welcome", n)
}("Gophers")
}
在上面的程式中,字串引數傳遞給第10行中的匿名函式。執行此程式將列印,
Welcome Gophers
使用者定義的函式型別
就像我們定義自己的結構型別一樣,可以定義自己的函式型別。
type add func(a int, b int) int
上面的程式碼片段建立了一個新的函式型別add
,它接受兩個整數引數並返回一個整數。 現在我們可以定義add
型別的變數。
讓我們編寫一個程式來定義add
型別的變數。
package main
import (
"fmt"
)
type add func(a int, b int) int
func main() {
var a add = func(a int, b int) int {
return a + b
}
s := a(5, 6)
fmt.Println("Sum", s)
}
在上面的程式中,在第10行中,我們定義了一個型別為add
的變數a
,併為其賦予一個簽名與add
型別匹配的函式。將結果分配給s。 這個程式將列印,
Sum 11
高階函式
來自wiki的高階函式的定義是至少執行以下之一的函式
- 將一個或多個函式作為引數
- 返回一個函式作為結果
讓我們看一下上面兩個場景的一些簡單例子。
將函式作為引數傳遞給其他函式
package main
import (
"fmt"
)
func simple(a func(a, b int) int) {
fmt.Println(a(60, 7))
}
func main() {
f := func(a, b int) int {
return a + b
}
simple(f)
}
在上面的例子中,在第7行中我們定義一個函式simple
,它接受一個函式,該函式接受兩個int引數並返回一個int作為引數。 在主函式裡面建立一個匿名函式f
,其簽名與函式simple
的引數匹配。 我們呼叫simple
,並在下一行中將f
作為引數傳遞給它。 該程式列印67作為輸出。
從其他函式返回函式
現在讓我們重寫上面的程式並從simple
函式返回一個函式。
package main
import (
"fmt"
)
func simple() func(a, b int) int {
f := func(a, b int) int {
return a + b
}
return f
}
func main() {
s := simple()
fmt.Println(s(60, 7))
}
在上面的程式中,第7行中的simple
函式返回一個函式,該函式接受兩個int引數並返回一個int引數。
這個簡單的函式從第15行呼叫。 simple
的返回值分配給s
。 現在s
包含簡單函式返回的函式。 我們呼叫s
並在第16行中傳遞兩個int引數。該程式輸出67。
閉包
閉包是匿名函式的特例。 閉包是匿名函式,用於訪問函式體外定義的變數。
一個例子可以使事情變得更加清晰。
package main
import (
"fmt"
)
func main() {
a := 5
func() {
fmt.Println("a =", a)
}()
}
在上面的程式中,匿名函式訪問變數a,該變數存在於匿名函式之外。 因此,這個匿名函式是一個閉包。
每個閉包都繫結到它自己變數。 讓我們通過一個簡單的例子來理解這意味著什麼。
package main
import (
"fmt"
)
func appendStr() func(string) string {
t := "Hello"
c := func(b string) string {
t = t + " " + b
return t
}
return c
}
func main() {
a := appendStr()
b := appendStr()
fmt.Println(a("World"))
fmt.Println(b("Everyone"))
fmt.Println(a("Gopher"))
fmt.Println(b("!"))
}
在上面的程式中,函式appendStr
返回一個閉包。該閉包繫結到變數t
。讓我們理解這意味著什麼。
變數a
和b
在第17和18行中宣告為閉包,它們受到自己t
值的約束。
我們首先使用引數World
呼叫a
。 現在,t
的值變成了Hello World
。
我們用引數Everyone
呼叫b
。 由於b
與其自己的變數t
繫結,因此b
的t
初始值為Hello
。 因此,在此函式呼叫之後,b
的t
值變為Hello Everyone
。
這個程式將列印,
Hello World
Hello Everyone
Hello World Gopher
Hello Everyone !
一類函式的使用
到目前為止,我們已經定義了一類函式,我們已經看到一些例子來了解它們的工作原理。 現在讓我們編寫一個具體的程式,它顯示了第一類函式的實際用法。
我們將建立一個程式,根據某些標準過濾一部分學生。 讓我們一步一步地解決這個問題。
首先讓我們定義學生型別。
type student struct {
firstName string
lastName string
grade string
country string
}
下一步是編寫過濾器功能。 該功能需要一部分學生和一個確定學生是否將過濾標準作為引數匹配的函式。 一旦我們編寫這個函式,我們會更好地理解。 讓我們繼續吧。
func filter(s []student, f func(student) bool) []student {
var r []student
for _, v := range s {
if f(v) == true {
r = append(r, v)
}
}
return r
}
在上面的函式中,要過濾的第二個引數是一個以學生為引數並返回bool的函式。 此功能確定特定學生是否符合標準。 我們在第一行中迭代學生切片。 我們將每個學生作為引數傳遞給函式f
。 如果返回true,則表示學生已通過過濾條件,並將其新增到結果切片r
中。 您可能對此功能的實際使用感到有些困惑,但是一旦我們完成該程式就會很清楚。 我添加了主函式,並在下面提供了完整的程式。
package main
import (
"fmt"
)
type student struct {
firstName string
lastName string
grade string
country string
}
func filter(s []student, f func(student) bool) []student {
var r []student
for _, v := range s {
if f(v) == true {
r = append(r, v)
}
}
return r
}
func main() {
s1 := student{
firstName: "Naveen",
lastName: "Ramanathan",
grade: "A",
country: "India",
}
s2 := student{
firstName: "Samuel",
lastName: "Johnson",
grade: "B",
country: "USA",
}
s := []student{s1, s2}
f := filter(s, func(s student) bool {
if s.grade == "B" {
return true
}
return false
})
fmt.Println(f)
}
在main
函式中,我們首先建立兩個學生s1
和s2
並將它們新增到切片s
中。 現在讓我們找到所有B級學生。我們在上面的程式中通過傳遞一個函式來確定學生是否有B級,如果是,那麼返回true,作為引數過濾函式,上述程式將列印,
[{Samuel Johnson B USA}]
假設我們想要找到所有來自印度的學生。 通過將函式引數更改為過濾函式,可以輕鬆完成此操作。
我提供了以下程式碼,
c := filter(s, func(s student) bool {
if s.country == "India" {
return true
}
return false
})
fmt.Println(c)
請將其新增到主函式並檢查輸出。
讓我們再寫一個程式來結束本節。 該程式將對切片的每個元素執行相同的操作並返回結果。 例如,如果我們想要將切片中的所有整數乘以5並返回輸出,則可以使用第一類函式輕鬆完成。 這些對集合的每個元素進行操作的函式稱為對映函式。 我已經提供了以下程式。
package main
import (
"fmt"
)
func iMap(s []int, f func(int) int) []int {
var r []int
for _, v := range s {
r = append(r, f(v))
}
return r
}
func main() {
a := []int{5, 6, 7, 8, 9}
r := iMap(a, func(n int) int {
return n * 5
})
fmt.Println(r)
}
以上程式將列印,
[25 30 35 40 45]