- 函数作为另一个函数的参数(典型用法:回调函数
函数可以返回另一个函数(典型用法:闭包
PS: 函数可以作为一个值赋给变量
函数作为参数
package main
import (
"fmt"
)
func added(msg string, a func(a, b int) int) {
fmt.Println(msg, a(1, 2))
}
func main() {
//go 函数内部不能嵌套命名函数
f := func(a, b int) int {
return a + b
}
// f 是函数作为参数
added("a+b=", f) //output: a+b= 3
added("a+b=", func(a, b int) int {
return a + b
}) //output: a+b= 3
}
回调函数
package main
import (
"fmt"
"sort"
)
// func(a,b int) 作为返回值
func added() func(a, b int) int {
//返回值是函数 只能是匿名函数 因为函数无法嵌套
f := func(a, b int) int {
return a + b
}
return f
}
func main() {
s1 := []int{112, 22, 52, 32, 12}
less := func(i, j int) bool {
return s1[i] > s1[j] //表示降序
}
// 这里的排序函数就是回调函数,每取一次i和j对应的元素,就调用一次less函数
sort.SliceStable(s1, less)
fmt.Println(s1)
//可以直接使用匿名函数 作为参数
sort.SliceStable(s1, func(i, j int) bool {
return s1[i] < s1[j] //表示升序
})
fmt.Println(s1)
}
函数作为返回值
package main
import (
"fmt"
)
// func(a,b int) 作为返回值
func added() func(a, b int) int {
//返回值是函数 只能是匿名函数 因为函数无法嵌套
f := func(a, b int) int {
return a + b
}
return f
}
func main() {
//调用该函数(无参)其实就是把added本该有的返回值赋给m
m := added()
fmt.Println(m(1, 2) == added()(1, 2)) //output: true
fmt.Printf("%T\n", m) //output: func(int, int) int 说明m是一个带有返回值的有参函数
fmt.Println(m(1, 2)) //output: 3
fmt.Printf("%T\n", added()) //output: func(int, int) int
fmt.Println(added()(3, 4)) //output: 7
}
回调:函数作为参数
package main
import "fmt"
type Callback func(x, y int) int
// 提供一个接口 方法具体实现不管,但是可以明确的是有个参数是函数名称(函数指针)
//其函数具体实现细节可以不用管
func test(x, y int, call Callback) int {
return call(x, y)
}
//回调函数的具体实现 即上面接口中方法参数的具体实现
func add(x, y int) int {
return x + y
}
func main() {
x, y := 1, 2
fmt.Println(test(x, y, add)) //output: 3
}
结合Error来看
package main
import (
"fmt"
"strconv"
)
type callback func(msg string)
func stringToInt(s string, callback callback) int {
//strconv.Atoi(s)是一个方法可以将string转换为int
//比如"123"转成123,如果是"fff"则无法转换,会报错parsing "qwer":invalid syntax
if value, err := strconv.Atoi(s); err != nil {
callback(err.Error())
return 0
} else {
return value
}
}
func errorLog(msg string) {
fmt.Println("输出日志", msg)
}
func main() {
fmt.Println(stringToInt("13", errorLog))
fmt.Println(stringToInt("qwer", errorLog))
}
闭包
package main
import "fmt"
func addAndMulti(x int) func(a, b int) int {
f := func(a, b int) int {
return (a + b) * x
}
return f
}
func main() {
//直接调用闭包 也叫匿名闭包
fmt.Println(addAndMulti(4)(2, 3)) //output: 20
//其他调用方式
//将函数的返回结果“闭包”赋值给变量f
f := addAndMulti(1)
//调用存储在变量中的闭包函数
fmt.Println(f(2, 3)) //output:5
}
闭包也叫工厂函数
package main
import "fmt"
func shareFirst(b int) func(int) int {
f := func(a int) int {
return a * b
}
return f
}
func main() {
//这个变量必须和下面的闭包在同一作用域环境下才可以对吧?
var y int
//闭包函数s
//g访问了不属于自己的变量y,在这个闭包函数中y是还没有赋值的,也就是自由变量
//所以如果把下面的这个闭包函数写在另一个函数里面去作为返回函数,自由变量作为参数
//就可以把一个自由变量赋值给多个不同的闭包函数了,相当于提前共享了自由变量
s := func(x int) int {
return x + y
}
y = 1
//调用闭包函数s
fmt.Println(s(1))
//说明通过这样其实可以提前共享了变量,然后再根据闭包函数的参数不同生成不同的值
//其实就是很多闭包函数共享一个变量的值
//所以很多地方把闭包函数也叫做工厂函数,类似于工厂方法
//第一步给自由变量绑定不同的值,其次多个闭包函数可以共享第一步绑定的自由变量的值
test := shareFirst(2)
fmt.Println(test(2)) // 2
fmt.Println(test(3)) // 4
fmt.Println(test(4)) // 6
}
Go指南中的样例
```go package main
import ( “fmt” “math” )
// 这里是 f 即作为compute函数的参数 又作为compute函数的值来返回 func compute(f func(float64, float64) float64) float64 { return f(3, 4) // 作为 值返回 }
func main() { hypot := func(x, y float64) float64 { return math.Sqrt(xx + yy) } fmt.Println(hypot(5, 12))
fmt.Println(compute(hypot)) // 刚好调用的时候也需要两个值 等价于 hypot(3,4)
// 上一句等价于下面这句
fmt.Println(hypot(3, 4))
//Pow作为函数可以当作变量来用,所以没有参数列表,看源码,里面是func Pow(x, y float64) float64
fmt.Println(compute(math.Pow))
}
```go
package main
import "fmt"
func add() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
s1, s2 := add(), add()
for i := 0; i < 5; i++ {
fmt.Println(s1(i), s2(-2*i))
}
}
总结:
- 回调和闭包是不一样的,回调是指可以调用一个函数,就比如写接口只是定义了有这个方法(里面啥类型都不用说,在之前用type定义了这个方法的参数类型,实现没讲,但是后面调用的时候也不用非得调用这个方法名,这个方法名在这里也只是一个参数) 具体方法调用的时候调用哪个方法是需要在使用把回调函数当参数那个方法来选就行,参数匹配就行。
- 闭包的话是把函数当作返回值,其实就是相当于自己除了自己定义的形参可以用之外,还可以使用其他方法的结果来当作自己的参数,如 f(2)(0, 1) 后面那个括号就相当于是调用别人的结果来算的,自己的参数只有自己第一个括号里的。