- 在 Go 里,函数是头等的,它可以用在整数、字符串或其它类型能用的地方:
- 将函数赋给变量
- 将函数作为参数传递给函数
- 将函数作为函数的返回类型
将函数赋给变量
- 变量 sensor 就是一个函数,而不是函数执行的结果
- 无论 sensor 的值是 fakeSensor 还是 realSensor,都可以通过 sensor() 来调用
- sensor 这个变量的类型是函数,该函数没有参数,返回一个 kelvin 类型的值。
- 换一种声明形式的话:
将函数传递给其它函数
package main
import (
"fmt"
"math/rand"
"time"
)
type kelvin float64
func measureTemperature(samples int, sensor func() kelvin) {
for i := 0; i < samples; i++ {
k := sensor()
fmt.Printf("%vº K\n", k)
time.Sleep(time.Second)
}
}
func fakeSensor() kelvin {
return kelvin(rand.Intn(151) + 150)
}
func main() {
measureTemperature(3, fakeSensor)
}
声明函数类型
- 为函数声明类型有助于精简和明确调用者的代码。
- 例如:type sensor func() kelvin
- 所以:func measureTemperature(samples int, s func() kelvin)
- 可以精简为:func measureTemperature(samples int, s sensor)
闭包和匿名函数
- 匿名函数就是没有名字的函数,在 Go 里也称作函数字面值。
- 因为函数字面值需要保留外部作用域的变量引用,所以函数字面值都是闭包的。
package main
import "fmt"
type kelvin float64
// sensor function type
type sensor func() kelvin
func realSensor() kelvin {
return 0
}
func calibrate(s sensor, offset kelvin) sensor {
return func() kelvin {
return s() + offset
}
}
func main() {
sensor := calibrate(realSensor, 5)
fmt.Println(sensor())
}
- 闭包(closure)就是由于匿名函数封闭并包围作用域中的变量而得名的。
作业题
- 修改这段程序:
- 声明一个变量,并将其用作 calibrate 函数的 offset 实参,而不是使用字面值数字 5。在此之后,即使修改变量,调用 sensor() 的结果也仍然为 5。这是因为 offset 形参接受的是实参的副本而不是引用,也就是所谓的按值传递。
- 使用 calibrate 函数和今天讲的 fakeSensor 函数以创建新的 sensor 函数,然后多次调用这个新的 sensor 函数,看看它是否每次都会调用 fakeSensor 函数并产生随机的读数。
package main
import (
"fmt"
"math/rand"
)
type kelvin float64
type sensor func() kelvin
func fakeSensor() kelvin {
return kelvin(rand.Intn(151) + 150)
}
func calibrate(s sensor, offset kelvin) sensor {
return func() kelvin {
return s() + offset
}
}
func main() {
var offset kelvin = 5
sensor := calibrate(fakeSensor, offset)
for count := 0; count < 10; count++ {
fmt.Println(sensor())
}
}